﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpPcap;
using SharpPcap.WinPcap;
using System.Timers;
using System.Threading;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using System.Drawing;
using System.Drawing.Imaging;
using System.Net.NetworkInformation;
using Newtonsoft.Json;
using BasePlatformCommon;
using System.Xml;
using BasePlatformCommon.lwf;
using Microsoft.Win32;
using System.Windows.Forms;
using BasePlatformCommon.devChain;

namespace BasePlatformNet
{
	public class BaseCoreNet
	{
		public bool typeLogEnable = true;							// 日志全局使能
		public bool normalLogEnable = false;						// 固定日志使能
		public bool testLogEnable = false;							// 测试日志使能
        public bool detectNetLogEnable = false;						// 检测日志使能
        public bool debugLogEnable = false;                        // 调试日志使能

		private int timeoutCount = 0;								// 超时计数器

		private bool useWinPcap = true;								// 使用 winpcap 发包

		private SocketSendMode socketSendMode = SocketSendMode.Asyn;// socket 发包模式：同步/异步

		private static BaseCoreNet baseCoreNetUnique = null;		// BaseCoreNet 单例

		private List<NetDevice> netDeviceList = null;				// SharpPcap 网络设备列表

		private bool netDevicesStarted = false;						// 网络设备开启标记

		private int readTimeoutMilliseconds = 50;					// 网口读取操作的超时时间

		private uint kernelBufferSize = (uint)(2048 * 4 * 4 * 4 * 4 * 4);	// SharpPcap 内核缓存

		private string sendMode = SendMode.ActiveOnly;				// 发送模式, 默认为只向活动网口发送

		private Random myRandom = null;								// 随机数生成器

		private string fatalMessage = "";							// 重大错误信息（底层 winpcap 不可用）

		private bool topologyDetectEnable = false;  				// 拓扑结构检测使能

        private int lowestGray = 0;                                  // 低灰过滤检测
		// 通信平台标志位（备份） 
		private bool backup_TopologyDetectEnable = false;							// 在线检测状态
		private CaptureSendState backup_captureSendState = CaptureSendState.Stop;	// 截屏发送状态

        private NetSpeed netSpeed = NetSpeed.GigaByte;              // 网络速度配置：千兆/百兆

		#region 自动发送帧包 + [可选]分控配置包 + 显示设置包

		private FramePacketPara framePacketPara = new FramePacketPara();					// 帧包参数对象

		private DispConfigPacketPara dispConfigPacketPara = new DispConfigPacketPara();		// 显示设置包参数对象

		private bool subConfigSendEnable = false;											// 分控配置数据周期发送使能

		#endregion

		#region 拓扑结构检测变量

		private bool topologyDetect_Started = false;				// 拓扑结构检测, 线程启动标志

		private System.Timers.Timer detectTopologyTimer = null;		// 在线检测计时器
        private Thread detectTopologyTimerArt = null;               // 在线检测计时器 ArtNet方式

		private bool topologyDetect_Processing = false;				// 某次拓扑结构检测执行中
        private int topologyDetect_LoopNum = 0;						// 检测循环数量
        private int topologyDetect_Art_LoopNum = 0;					// 检测循环数量 ArtNet方式
        private int topologyDetect_TimeSpan_ms = 100;				// 拓扑结构后台检测时间间隔, 默认100ms
        private int topologyDetect_Art_TimeSpan_ms = 3000;			// 拓扑结构后台检测时间间隔 ArtNet方式, 默认3000ms
		private int topologyDetect_Wait_ms = 200;					// 回包等待时间, 默认200ms

		private int maxSubCtrlNum = 255;							// 最大分控数量 - 最大255
		private int maxEquipmentNum = 63;							// 最大附属设备数量 - 最大63

		private int currentOperFlag = 0;							// 双缓冲操作标记
        private int currentReadFlag = -1;

        private DetectType detectType = DetectType.Normal;          // 在线检测模式

		private DetectMode detectMode = DetectMode.AllDetect;		// 网口活跃检测模式, 若为true, 则只检测深度为 1 的分控和附属设备

		private bool sendBlackDataBeforeStartTopoDetect = true;		// 在启动在线检测前, 是否发送 4 帧 320*240 的黑数据

		private bool firstResetPacketNumCommandHasSended = false; 

		#endregion

		#region 截屏与下发显示数据

		private Thread captureSendThread = null;					// 截屏与下发线程

		private CaptureSendState captureSendState = CaptureSendState.Stop;	// 截屏发送线程的运行状态

		private Point captureLocation = new Point(0, 0);			// 截屏区域左上角位置

		private Point captureSize = new Point(320, 240);			// 截屏区域大小

		private int fps = 30;										// 截屏发送的帧频

		private int sleepTimeBase = 33;								// 对应 fps 的截屏线程休眠时间（ms），sleepTimeBase = 1000 / fps

		private byte[] frameBuffer_RGB = null;						// 内部结构

		private Stopwatch sw_total = new Stopwatch();				// 截屏发送总计时器

		private int captureExceptionNum = 0;						// 截屏发送异常计数器
		private bool showExceptionWhileCapture = false;				// 截屏发送期间的异常

		private bool autoSendCaptureDataByDetectResult = false;		// 是否根据检测结果自动开始/停止截屏下发
		private int detectNoDeviceNum = 0;							// 未检测到活跃设备的检测循环次数
		private long lastStartTicksImage = 0;						// 前次发送显示数据的时间

        private bool isSending = false;

        private object sendLocker = new object();

        private int currFrameTotal = 0;

        private int lastFrameCount = 0;

        private bool isFrameCountTimerRunning = false;

		// 委托定义与声明
		public delegate void SendOneFrameFromBufferDelegate(byte[] buffer, int lineNum, int rowNum);

		#endregion
        
		#region 读写分控、附属设备变量

		private int readSubTimeout_ms = 1000;						// 读分控超时时间
		private int readEquTimeout_ms = 1000;						// 读附属设备超时时间

		private const int readSubMemPackageIdMin = 1000;			// 读分控包序号下限
		private const int readSubMemPackageIdMax = 2999;			// 读分控包序号上限
		private int readSubMemPackageId = readSubMemPackageIdMin;	// 读分控包序号

		private const int writeSubMemPackageIdMin = 3000;			// 写分控包序号下限
		private const int writeSubMemPackageIdMax = 4999;			// 写分控包序号上限
		private int writeSubMemPackageId = writeSubMemPackageIdMin;	// 写分控包序号

		private const int readEquMemPackageIdMin = 5000;			// 读附属设备包序号下限
		private const int readEquMemPackageIdMax = 6999;			// 读附属设备包序号上限
		private int readEquMemPackageId = readEquMemPackageIdMin;	// 读附属设备包序号

		private const int writeEquMemPackageIdMin = 7000;			// 写附属设备包序号下限
		private const int writeEquMemPackageIdMax = 8999;			// 写附属设备包序号上限
		private int writeEquMemPackageId = writeEquMemPackageIdMin;	// 写附属设备包序号

		#endregion

		#region 读写Flash

		private uint blockSize = 4 * 1024;				// block 大小：4KB

		private int updateProcessValue = 0;				// 在线升级进度 0-100

		private int eraseTimeSpan = 400;				// 擦除时间间隔

		private int updateTimeSpan = 50;				// 发送升级包时间间隔

		private int updateDoneNum = 0;					// 在线升级完成数量

		private bool forceStop = false;					// 强制停止标记

		private int readFlashProcessValue = 0;			// 读取Flash进度

		#endregion

		#region 支持多路交换机

		private bool useSwitch = false;					// 是否支持交换机

		private string switchConfigFilePath = "";		// 多路交换机配置文件

		public static int maxSubEquNum = 1000;			// 多路交换机配置文件 - 最大分控数量

		// 多路交换机配置文件 - 内部数据结构
		private List<IdMacPair> idMacList = null;
		private List<string> macList = null;

		// 行映射表、分控映射表、附属设备映射表
		private List<MacTable> rowMacTable = null;
		private List<MacTable> subMacTable = null;
		private List<MacTable> equMacTable = null;

		#endregion

		#region 异形映射

		private byte[] binData = null;					// 内部异形格式数组 - 坐标映射

		private byte[] binDataChannel = null;			// 内部异形格式数组 - 通道映射

		private bool lwfHasPortInfoData = false;		// lwf文件中是否存在端口灯具类型数据

		private bool lwfHasChannelInfoData = false;		// lwf文件中是否存在通道映射数据

		List<ColorMode> portColorModeList = null;		// 端口灯具类型列表

		private AddrMapJson addrMapJson = null;			// 异形映射数据（Json格式）

		private int[] addrMap = null;					// 异形地址映射表 - 对应像素首地址

		private int[] addrMapOffset = null;				// 异形地址映射表 - 对应像素的偏移量：0-R, G-1, B-2, N-3, W-4, Y-5

		private int[] addrMapType = null;				// 异形地址映射表 - 对应的映射类型

		private int[] addrMapPortTag = null;			// 异形地址映射表 - 端口标签

		private bool addrMapIsBuilding = false;			// 正在生成异形地址映射表

		private int subNumInBin1 = 0;					// lwf文件中解析的分控数量 - Bin-1区

		private int subNumInBin2 = 0;					// lwf文件中解析的分控数量 - Bin-2区

		private int maxLampNumInBin = 0;				// lwf文件中解析的端口最大带灯数

		private int out_l, out_h, out_pixel;			// 输出分辨率

        private int portChannelMax = 0;                 // 端口最大通道数

        private int channelNum = 3;						// 灯具通道数

		private int channelNumTotal = 0;				// 灯具通道数

		private MapSrcType baseMapSrcType = MapSrcType.NoMap;		// 异形映射数据来源, 无异形/lwf/json

		private MapMode baseMapMode = MapMode.Baned;				// 异形映射模式, 默认三色模式

		private string baseColorString = "RGB";						// 异形映射灯具排列

		private List<DMainControl> declareMainControlList = null;	// 由布线文件声明的设备链

		private bool declareInfoIsBuilding = false;					// 由布线文件声明的设备链正在填充

		private bool downloadLwfWaitFlag = false;					// 下载布线文件等待标记

		private bool downloadLwfBreakFlag = false;					// 下载布线文件强制结束标记

		private Point lwfSystemArea = new Point(-1, -1);			// lwf格式布线文件中的画布大小

		private int userColorType = 0;								// 用户在界面上选择的颜色模式，0-禁用，1-四色纯，2-四色增强

        private int userChannelLvl = 0;                             // 用户在界面上选择的通道强度

		private bool addrMapDatPlayingMask = false;					// DatPlayForm 播放掩码

		private bool addrMapEnable = false;			// 界面设置的异形使能状态

		#endregion

		#region 灯具巡检

		private bool lampCheckProcessing = false;					// 灯具巡检标记

		#endregion

		#region 子控（解码器）

		private int miniCommNum = 0;								// 子控通信包数量

		#endregion

		#region 网格屏配置

		public int[,] gridConfigValue = null;

		#endregion

		#region LedDebug

		private int effectLedDebugSub = 0;							// 由 LedDebug 设置的有效分控
		private int effectLedDebugPort = 0;							// 由 LedDebug 设置的有效端口

		#endregion

        private List<string> ignoreMacList;
        private List<string> effectMacList;
        
		public event EventHandler<WriteEventArgs> WriteFlashStateUpdated;	// 【事件】批量写入Flash - 进度更新
		public event EventHandler<ReadEventArgs> ReadFlashStateUpdated;		// 【事件】批量读取Flash - 进度更新
		public event EventHandler<WriteEventArgs> WriteSubStateUpdated;		// 【事件】写入分控 - 进度更新
        public delegate void UserColorChanged(int userCColorType);          // 【委托】用户设置颜色变化
        public UserColorChanged userColorChangedHandle;
        public delegate void LowestGrayChanged();                           // 【委托】低灰过滤设置保存
        public LowestGrayChanged lowestGrayChangedHandle;
        public Action<string> UpdateStatusStr;                              // 【事件】更新状态栏消息
        public Action updateMainTreeArtNet;
        
		private string splitLine = "------------------------------------------------";

		private string section = "CoreNet";									// 多语言文件配置节名称
		
		private string tip = "提示";											// MessageBox 题头

		/****************************************************************************************************/

		#region 内部逻辑

		#region 单例模式

		// 构造函数 - 单例模式, 私有化构造函数
        private BaseCoreNet(bool normalLogEnable, bool testLogEnable, bool detectNetLogEnable, bool debugLogEnable, List<string> ignoreMacList, List<string> effectMacList) {
            // 设置日志使能状态
            this.normalLogEnable = normalLogEnable;
            this.testLogEnable = testLogEnable;
            this.detectNetLogEnable = detectNetLogEnable;
            this.debugLogEnable = debugLogEnable;

            // 打印 BaseCoreNet 版本
            //var version = getVersion();
            //typeLog(version, LogFile.Normal, LogTarget.FileOnly);

            // 初始化随机数生成器
            myRandom = new Random();

            // 初始化网络设备列表
            initDevices(ignoreMacList, effectMacList);

            this.ignoreMacList = new List<string>(ignoreMacList);
            this.effectMacList = new List<string>(effectMacList);

            // 打开所有网络设备
            openAllDevices();

            // 启动截屏发送线程
            captureSendThread = new Thread(captureSendProcess);
            captureSendThread.IsBackground = true;
            captureSendThread.Priority = ThreadPriority.Highest;
            captureSendThread.Start();
            captureSendState = CaptureSendState.Run;

            // 初始化在线检测计时器
            detectTopologyTimer = new System.Timers.Timer();

            // 启动在线检测计时器 - 空转
            startTopologyDetectThread();

            // 初始化端口灯具类型
            portColorModeList = new List<ColorMode>();

            // 初始化由布线文件声明的设备链
            declareMainControlList = new List<DMainControl>();

            // 初始化 mac 映射表
            rowMacTable = new List<MacTable>();
            subMacTable = new List<MacTable>();
            equMacTable = new List<MacTable>();

            // 生成地址映射表 - 默认关闭异形
            buildAddrMapTable(MapSrcType.NoMap);

            rdmCore = new RDMCore(0);

            var requeryRDMThread = new Thread(requeryRDMProcess);
            requeryRDMThread.IsBackground = true;
            requeryRDMThread.Start();

            ArtDevList_uu = new ArtPortInfo[32768];
            for (int i = 0; i < 32768; i++)
            {
                //初始化universe数据
                ArtPortInfo info = new ArtPortInfo();
                info.ip = " ";
                info.mac = " ";
                info.bindindex = i & 0x07;
                info.net = (byte)(i >> 8);
                info.address = (byte)(i & 0xFF);
                info.UIDList = null;

                ArtDevList_uu[i] = info;
            }
        }

		// 获得应用程序 exe 所在路径
		private string getAppPath()
		{
			string path = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;

			path = path.Substring(0, path.LastIndexOf("\\"));

			return path;
		}

		#endregion

		#region 操作网络设备

		// 初始化网络设备列表
		private void initDevices(List<string> ignoreMacList, List<string> effectMacList)
		{
			netDevicesStarted = false;

			if (netDeviceList == null)
			{
				netDeviceList = new List<NetDevice>();
			}
			else
			{
				netDeviceList.Clear();
			}

			try
			{
				var devices = CaptureDeviceList.Instance;	// 获取网络设备

				if (devices.Count < 1)
				{
					string noNetDevice = "本机找不到网络设备";
					if (LanguageOper.available == true)
					{
						noNetDevice = LanguageOper.ReadValue(section, "logStr.0");
					}
					typeLog(noNetDevice);

					return;
				}

				List<NetworkInterfaceInfo> networkInterfaceInfoList = new List<NetworkInterfaceInfo>();

				NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
				foreach (NetworkInterface netAdapter in networkInterfaces)
				{
					NetworkInterfaceInfo info = new NetworkInterfaceInfo();

					// 网卡名称
					info.name = netAdapter.Id;

					// IP地址
					if (netAdapter.NetworkInterfaceType == NetworkInterfaceType.Ethernet)		//判断是否是以太网连接
					{
						IPInterfaceProperties ip = netAdapter.GetIPProperties();     // IP配置信息
						if (ip.UnicastAddresses.Count > 0)
						{
							for (int i = 0; i < ip.UnicastAddresses.Count; i++)
							{
								if (ip.UnicastAddresses[i].Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
								{
									info.ipAddress = ip.UnicastAddresses[i].Address.ToString();		// IPV4地址
								}
							}
						}
						else
						{
							info.ipAddress = "";
						}
					}

					// 判断是否为无线网卡
					try
					{
						bool isWireless = false;

						if (netAdapter.Description.ToLower().Contains("wireless"))
						{
							isWireless = true;
						}
						else
						{
							string fRegistryKey = @"SYSTEM\CurrentControlSet\Control\Network\" + netAdapter.Id + @"\Connection";
							RegistryKey rk = Registry.LocalMachine.OpenSubKey(fRegistryKey, false);
							if (rk != null)
							{
								int mediaSubType = Convert.ToInt32(rk.GetValue("MediaSubType", 0));
								if (mediaSubType == 2)
								{
									isWireless = true;
								}
							}
						}

						info.isWireless = isWireless;
					}
					catch (Exception)
					{
						continue;
					}

					networkInterfaceInfoList.Add(info);
				}

				int effectDevNum = 0;
				for (int i = 0; i < devices.Count; i++)
				{
					WinPcapDevice dev = (WinPcapDevice)devices[i];
					NetDevice curDev = null;

					// 排除忽略的网卡（按 mac 地址）
					bool isIgnorNetworkCard = false;
					foreach (string mac in ignoreMacList)
					{
						if (mac.Equals(dev.Interface.MacAddress.ToString().ToUpper()))
						{
							isIgnorNetworkCard = true;
						}
					}
					if (isIgnorNetworkCard == true)
					{
						continue;
					}

					// 排除无线网卡
					bool isWireless = false;
					string ipAddress = "";
					foreach (NetworkInterfaceInfo info in networkInterfaceInfoList)
					{
						if (dev.Name.Contains(info.name))
						{
							isWireless = info.isWireless;
							ipAddress = info.ipAddress;
							break;
						}
					}
					if (isWireless == true)
					{
						continue;
					}

					// 配置了有效网卡mac：按照指定网卡列表过滤
					if (effectMacList.Count > 0)
					{
						bool isEffect = false;
						foreach (string mac in effectMacList)
						{
							if (mac.Equals(dev.Interface.MacAddress.ToString().ToUpper()))
							{
								isEffect = true;
							}
						}
						if (isEffect == false)
						{
							continue;
						}
					}

					string disString = "";
					try
					{
						if (dev.Interface == null)
						{
							typeLog("[Error] dev " + effectDevNum + " Interface is NULL");
						}
						else if (dev.Interface.MacAddress == null)
						{
							typeLog("[Error] dev " + effectDevNum + " MacAddress is NULL");
						}
						else
						{
							disString = String.Format("{0} | MAC: {1}", dev.Interface.FriendlyName.PadRight(6, ' '), dev.Interface.MacAddress.ToString());
							curDev = new NetDevice(dev.Interface.MacAddress.ToString());
							curDev.setParaObjs(framePacketPara, dispConfigPacketPara);	// 设置设备发包参数对象
						}
					}
					catch (Exception e)
					{
						typeLog("[Error] " + e.Message + ", " + e.StackTrace);
					}

					if (curDev != null)
					{
						string nameStr = string.Format("网卡 {0}: {1}", effectDevNum, disString);
						if (LanguageOper.available == true)
						{
							nameStr = string.Format(LanguageOper.ReadValue(section, "str.0"), effectDevNum, disString);
						}
						
						string name = nameStr;

						curDev.index = effectDevNum;
						curDev.pcapDevice = dev;
						curDev.name = name;
						curDev.ip = ipAddress;

						curDev.packetSender.setDevice(dev);
						curDev.packetSender.setIPAddress(ipAddress);
                        curDev.packetSender.setNetDevice(curDev);

                        curDev.packetReceiver.setDevice(curDev);
                        

						netDeviceList.Add(curDev);

						effectDevNum++;	// 当前网卡为参与业务逻辑的网卡
					}
				}

				if (netDeviceList.Count == 0)
				{
					string msg = "未找到有效网卡";
					if (LanguageOper.available == true)
					{
						msg = LanguageOper.ReadValue(section, "msgStr.0");
					}
					MessageBox.Show(msg, tip, MessageBoxButtons.OK, MessageBoxIcon.Warning);
				}
				else
				{
					foreach (NetDevice dev in netDeviceList)
					{
						typeLog("[BaseCoreNet] dev.name = " + dev.name + ", ip = " + dev.ip);
					}
				}

				typeLog("[BaseCoreNet] initDevices done!");
			}
			catch (Exception e)
			{
				typeLog("[Error] " + e.Message + "\n" + e.StackTrace);
				fatalMessage = e.Message;
			}
		}

		// 打开所有网络设备
		private void openAllDevices()
		{
			if (netDevicesStarted)	// 防止重复打开网络设备
			{
				return;
			}

			for (int k = 0; k < netDeviceList.Count; k++)
			{
                //((NetDevice)netDeviceList[k]).packetSender.ExceptionHappen += restartDevice;
                ((NetDevice)netDeviceList[k]).packetReceiver.ArtMessageReceiveHandle += handleArtNetPacket;
				try
				{
					((NetDevice)netDeviceList[k]).pcapDevice.Open(SharpPcap.WinPcap.OpenFlags.NoCaptureLocal | SharpPcap.WinPcap.OpenFlags.Promiscuous, readTimeoutMilliseconds);
					Log.NormalLogger.Info(((NetDevice)netDeviceList[k]).name + " - Use Promiscuous Mode");
				}
				catch (Exception)
				{
					((NetDevice)netDeviceList[k]).pcapDevice.Open(SharpPcap.WinPcap.OpenFlags.NoCaptureLocal, readTimeoutMilliseconds);
					Log.NormalLogger.Info(((NetDevice)netDeviceList[k]).name + " - Use Normal Mode");
				}

				((NetDevice)netDeviceList[k]).pcapDevice.KernelBufferSize = kernelBufferSize;
				((NetDevice)netDeviceList[k]).pcapDevice.StartCapture();
			}

			netDevicesStarted = true;

			typeLog("[BaseCoreNet] openAllDevices done!");
		}

		// 关闭所有网络设备
		private void closeAllDevices()
		{
			if (!netDevicesStarted)
			{
				return;
			}

            for (int k = 0; k < netDeviceList.Count; k++) {
                try {
                    ((NetDevice)netDeviceList[k]).pcapDevice.StopCapture();
                } catch (Exception) {
                    ;
                } finally {
                    ((NetDevice)netDeviceList[k]).packetReceiver.ArtMessageReceiveHandle -= handleArtNetPacket;
                    //((NetDevice)netDeviceList[k]).packetSender.ExceptionHappen -= restartDevice;
                }
            }

			netDevicesStarted = false;
		}

		#endregion

		#region 在线检测

		// 启动在线检测定时器
		private void startTopologyDetectThread()
		{
			// 限制本方法只能调用一次
			if (topologyDetect_Started)
			{
				return;
			}

			// 初始化线检测定时器
			detectTopologyTimer.Interval = topologyDetect_TimeSpan_ms;		// 活动网口上的拓扑结构 - 检测间隔
			detectTopologyTimer.Elapsed -= new ElapsedEventHandler(detectTopologyTimer_Elapsed);
			detectTopologyTimer.Elapsed += new ElapsedEventHandler(detectTopologyTimer_Elapsed);

            detectTopologyTimerArt = new Thread(() => { detectTopologyTimerArt_Elapsed(); });
            detectTopologyTimerArt.IsBackground = true;

			// 更改标志位
			firstResetPacketNumCommandHasSended = false;
			topologyDetect_Started = true;

			// 其他相关变量
			topologyDetect_LoopNum = 0;		// 在线检测周期
            topologyDetect_Art_LoopNum = 0;

			// 启动线检测定时器
			detectTopologyTimer.Start();
            detectTopologyTimerArt.Start();
		}

		// 在线检测定时器动作 - 线程方法
		private void detectTopologyTimer_Elapsed(object sender, EventArgs e)
		{
			// 在线检测未使能
			if (topologyDetectEnable == false)
			{
				return;
			}

			// 不重入检测过程
			if (topologyDetect_Processing)
			{
				return;
			}

            //if (detectType != DetectType.Normal) {
            //    return;
            //}

			topologyDetect_Processing = true;

			topologyDetect_LoopNum++;
			if (topologyDetect_LoopNum > 1000000)
			{
				topologyDetect_LoopNum = 1;
			}

			// 每次轮询起始时，发送加载命令（全局），总包数、错误包数加载到输出寄存器
			byte[] openLoadData = new byte[256];
			openLoadData[0] = 0x02;
			int devIndex = getActiveDevIndex();
			if (devIndex > -1)
			{
                //Log.DebugLogger.Info("detectLoad");
				writeSubMem(devIndex, 0, 0x0, openLoadData, true);
            }

			// 设置标志位、重置网络设备环境
			while (currentReadFlag != -1)
			{
				Thread.Sleep(1);
			}
			if (this.currentOperFlag == 0)
			{
				this.currentOperFlag = 1;
			}
			else
			{
				this.currentOperFlag = 0;
			}
			for (int i = 0; i < netDeviceList.Count; i++)
			{
				NetDevice dev = (NetDevice)netDeviceList[i];
				dev.currentOperFlag = this.currentOperFlag;
				dev.clearSubEquList();
			}

			// 发送通信命令包 - 用于在线检测
			sendTopoDetectPacket();

			// 回包等待
			Thread.Sleep(topologyDetect_Wait_ms);

			// 收集检测结果
			IntPair detectNumPair = collectDetectResults();

            //RDM模式，收集灯具信息
            if (detectType == DetectType.ArtNet) {
                QueryUIDListTotal_Sub();
            }

            if (detectNumPair.val1 == -1 && detectNumPair.val2 == -1)
			{
				return;
			}
			else
			{
				/*
				maxSubCtrlNum = detectNumPair.val1 * 2;
				if (maxSubCtrlNum > 255)
				{
					maxSubCtrlNum = 255;
				}
				else if (maxSubCtrlNum < 8)
				{
					maxSubCtrlNum = 8;
				}

				maxEquipmentNum = detectNumPair.val2 * 2;
				if (maxEquipmentNum > 63)
				{
					maxEquipmentNum = 63;
				}
				else if (maxEquipmentNum < 8)
				{
					maxEquipmentNum = 8;
				}
				*/
			}
			
			bool hasActiveDevice = false;
			if (detectNumPair.val1 > 0 || detectNumPair.val2 > 0)
			{
				hasActiveDevice = true;
			}

			if (hasActiveDevice == true && firstResetPacketNumCommandHasSended == false)
			{
				sendClearPacketCommand(getActiveDevIndex());
				firstResetPacketNumCommandHasSended = true;
			}

			// 检测到活跃网口后，不再执行检测
			/*
			if (hasActiveDevice == true)
			{
				topologyDetectEnable = false;
			}
			*/

			// 根据是否检测到活跃设备, 自动开始/停止截屏输出
			if (autoSendCaptureDataByDetectResult)
			{
				if (hasActiveDevice == true)
				{
					captureSendState = CaptureSendState.Run;
					detectNoDeviceNum = 0;
				}
				else
				{
					if (detectNoDeviceNum > 3)
					{
						captureSendState = CaptureSendState.Stop;
						detectNoDeviceNum = 0;
					}
					else
					{
						detectNoDeviceNum++;
					}
				}
			}

			topologyDetect_Processing = false;
		}

        // 在线检测定时器动作 - 线程方法 ArtNet方式
        private void detectTopologyTimerArt_Elapsed() {
            while (true) {
                // 在线检测未使能
                if (topologyDetectEnable == false) {
                    Thread.Sleep(topologyDetect_Art_TimeSpan_ms);
                    continue;
                }

                // 不重入检测过程
                if (topologyDetect_Processing) {
                    Thread.Sleep(topologyDetect_Art_TimeSpan_ms);
                    continue;
                }

                if (detectType != DetectType.ArtNet)
                {
                    Thread.Sleep(topologyDetect_Art_TimeSpan_ms);
                    continue;
                }

                if (detectType != DetectType.Normal)
                {
                    Thread.Sleep(topologyDetect_Art_TimeSpan_ms);
                    continue;
                }

                topologyDetect_Processing = true;

                topologyDetect_Art_LoopNum++;
                if (topologyDetect_Art_LoopNum > 1000000) {
                    topologyDetect_Art_LoopNum = 1;
                }

                QueryArtSubControl();
                if (BaseArtDevDetailList == null || BaseArtDevDetailList.DetailList == null && !addrMapEnable) { //
                    
                } else {
                    var currSubNum = getSubControlNum();
                    var totalSubNum = 1;
                    if (BaseArtDevDetailList != null && BaseArtDevDetailList.DetailList != null) { //有基准则以基准设备链分控数为准
                        totalSubNum = BaseArtDevDetailList.DetailList.Count;
                    } else if (addrMapEnable) { //无基准则以异形为准
                        totalSubNum = getSubControlNumInLwf();
                    }

                    totalSubNum = Math.Max(1, totalSubNum);
                    if (totalSubNum != currSubNum) {
                        RequeryUIDListTotal();
                        Thread.Sleep(ArtRequeryWaitTime * 1000);
                    }
                }
                QueryUIDListTotal();

                if (updateMainTreeArtNet != null) {
                    updateMainTreeArtNet();
                }
                // 收集检测结果
                IntPair detectNumPair = collectDetectResultsArt();

                if (detectNumPair.val1 == -1 && detectNumPair.val2 == -1) {
                    return;
                } else {
                    /*
                    maxSubCtrlNum = detectNumPair.val1 * 2;
                    if (maxSubCtrlNum > 255)
                    {
                        maxSubCtrlNum = 255;
                    }
                    else if (maxSubCtrlNum < 8)
                    {
                        maxSubCtrlNum = 8;
                    }

                    maxEquipmentNum = detectNumPair.val2 * 2;
                    if (maxEquipmentNum > 63)
                    {
                        maxEquipmentNum = 63;
                    }
                    else if (maxEquipmentNum < 8)
                    {
                        maxEquipmentNum = 8;
                    }
                    */
                }

                bool hasActiveDevice = false;
                if (detectNumPair.val1 > 0 || detectNumPair.val2 > 0) {
                    hasActiveDevice = true;
                }

                //if (hasActiveDevice == true && firstResetPacketNumCommandHasSended == false) {
                //    sendClearPacketCommand(getActiveDevIndex());
                //    firstResetPacketNumCommandHasSended = true;
                //}

                // 检测到活跃网口后，不再执行检测
                /*
                if (hasActiveDevice == true)
                {
                    topologyDetectEnable = false;
                }
                */

                // 根据是否检测到活跃设备, 自动开始/停止截屏输出
                if (autoSendCaptureDataByDetectResult) {
                    if (hasActiveDevice == true) {
                        captureSendState = CaptureSendState.Run;
                        detectNoDeviceNum = 0;
                    } else {
                        if (detectNoDeviceNum > 3) {
                            captureSendState = CaptureSendState.Stop;
                            detectNoDeviceNum = 0;
                        } else {
                            detectNoDeviceNum++;
                        }
                    }
                }

                topologyDetect_Processing = false;

                Thread.Sleep(topologyDetect_Art_TimeSpan_ms);
            }
        }

		// 发送通信命令包 - 用于在线检测
		private void sendTopoDetectPacket()
		{
			// 发送读分控命令包
			int detectSubNum = (detectMode == DetectMode.AllDetect ? maxSubCtrlNum : 1);
			for (int k = 1; k <= detectSubNum; k++)
			{
				if (topologyDetectEnable == false)
				{
					break;
				}

				for (int i = 0; i < netDeviceList.Count; i++)
				{
					if (topologyDetectEnable == false)
					{
						break;
					}
					NetDevice dev = (NetDevice)netDeviceList[i];
					dev.packetSender.sendCommandPacket_DetectRead(InnerCommandTarget.SubCtrl, k, 0x0, topologyDetect_LoopNum);

                    //if (topologyDetect_LoopNum % 100 == 1) {
                    //    Log.DebugLogger.Info("sendDetectRead devIndex: " + i);
                    //}
				}
				
				Thread.Sleep(1);
			}

			// 发送读附属设备命令包
			int detectEquNum = (detectMode == DetectMode.AllDetect ? maxEquipmentNum : 1);
			for (int k = 1; k <= detectEquNum; k++)
			{
				if (topologyDetectEnable == false)
				{
					break;
				}

				for (int i = 0; i < netDeviceList.Count; i++)
				{
					if (topologyDetectEnable == false)
					{
						break;
					}
					NetDevice dev = (NetDevice)netDeviceList[i];
					dev.packetSender.sendCommandPacket_DetectRead(InnerCommandTarget.Equipment, k, 0x0, topologyDetect_LoopNum);
				}

				Thread.Sleep(1);
			}
		}

		// 收集检测结果 - 线程方法
		private IntPair collectDetectResults()
		{
			int detectedSubNum = 0;
			int detectedEquNum = 0;

			if (currentOperFlag == 1)
			{
				// 向各设备的 1 链设置值
				for (int k = 0; k < netDeviceList.Count; k++)
				{
					NetDevice curDevice = (NetDevice)netDeviceList[k];

					if (curDevice.subCtrlList_1.Count < curDevice.lastDetectSubNum_1)
					{
						curDevice.lostPacketTimes_1++;
						if (curDevice.lostPacketTimes_1 < 3)
						{
							continue;
						}
						else
						{
							curDevice.lastDetectSubNum_1 = curDevice.subCtrlList_1.Count;
							curDevice.lostPacketTimes_1 = 0;
						}
					}
					else
					{
						curDevice.lastDetectSubNum_1 = curDevice.subCtrlList_1.Count;
						curDevice.lostPacketTimes_1 = 0;
					}

					// 清除原有数据
					while (currentReadFlag != -1)
					{
						Thread.Sleep(1);
					}
					curDevice.ctrlNodeChain_1.Clear();

					// 设置分控在级联链中的位置
					for (int i = 0; i < curDevice.subCtrlList_1.Count; i++)
					{
						int position = curDevice.subCtrlList_1[i].mySubCtrlDepth;
						if (!curDevice.ctrlNodeChain_1.Contains(position))
						{
							curDevice.ctrlNodeChain_1[position] = curDevice.subCtrlList_1[i];
						}
						else
						{
							ControlNode firstNode = (ControlNode)(curDevice.ctrlNodeChain_1[position]);
							firstNode.brother.Add(curDevice.subCtrlList_1[i]);
							firstNode.brotherCount++;
							foreach (ControlNode b in firstNode.brother)
							{
								b.brotherCount = firstNode.brotherCount;
							}
						}
					}
					detectedSubNum = curDevice.subCtrlList_1.Count;

					// 设置附属设备在级联链中的位置
					for (int j = 0; j < curDevice.equipmentList_1.Count; j++)
					{
						int position = curDevice.equipmentList_1[j].myEquipmentDepth + 1000;
						if (!curDevice.ctrlNodeChain_1.Contains(position))
						{
							curDevice.ctrlNodeChain_1[position] = curDevice.equipmentList_1[j];
						}
						else
						{
							ControlNode firstNode = (ControlNode)(curDevice.ctrlNodeChain_1[position]);
							firstNode.brother.Add(curDevice.equipmentList_1[j]);
							firstNode.brotherCount++;
							foreach (ControlNode b in firstNode.brother)
							{
								b.brotherCount = firstNode.brotherCount;
							}
						}
					}
					detectedEquNum = curDevice.equipmentList_1.Count;

					// 判断分控编号是否连续
					foreach (int key in curDevice.ctrlNodeChain_1.Keys)
					{
						ControlNode node = ((ControlNode)curDevice.ctrlNodeChain_1[key]);
						// 分控
						if (node.type == 1)
						{
							int subCtrlDepth = node.mySubCtrlDepth;
							if (subCtrlDepth > 1 && !curDevice.ctrlNodeChain_1.ContainsKey(subCtrlDepth - 1))
							{
								node.noContinueFlag = true;
							}
						}
						// 附属设备
						else if (node.type == 0)
						{
							int equipmentDepth = node.myEquipmentDepth + 1000;
							if (equipmentDepth > 1000 && !curDevice.ctrlNodeChain_1.ContainsKey(equipmentDepth - 1))
							{
								node.noContinueFlag = true;
							}
						}
					}

					curDevice.totalCtrlNodeNum_1 = curDevice.ctrlNodeChain_1.Keys.Count;

					// 设置网口活跃状态
					if (curDevice.totalCtrlNodeNum_1 > 0)
					{
						curDevice.activeStatus = true;
					}
					else
					{
						curDevice.activeStatus = false;
					}

					if (topologyDetect_LoopNum % 600 == 0)
					{
						typeLog("Topology Detect: DeviceIndex = " + k + ", TotalSubNum = " + curDevice.totalCtrlNodeNum_1);
					}
				}
			}
			else
			{
				// 向各设备的 0 链设置值
				for (int k = 0; k < netDeviceList.Count; k++)
				{
					NetDevice curDevice = (NetDevice)netDeviceList[k];

					if (curDevice.subCtrlList_0.Count < curDevice.lastDetectSubNum_0)
					{
						curDevice.lostPacketTimes_0++;
						if (curDevice.lostPacketTimes_0 < 3)
						{
							continue;
						}
						else
						{
							curDevice.lastDetectSubNum_0 = curDevice.subCtrlList_0.Count;
							curDevice.lostPacketTimes_0 = 0;
						}
					}
					else
					{
						curDevice.lastDetectSubNum_0 = curDevice.subCtrlList_0.Count;
						curDevice.lostPacketTimes_0 = 0;
					}

					// 清除原有数据
					while (currentReadFlag != -1)
					{
						Thread.Sleep(1);
					}
					curDevice.ctrlNodeChain_0.Clear();

					// 设置分控在级联链中的位置
					for (int i = 0; i < curDevice.subCtrlList_0.Count; i++)
					{
						int position = curDevice.subCtrlList_0[i].mySubCtrlDepth;
						if (!curDevice.ctrlNodeChain_0.Contains(position))
						{
							curDevice.ctrlNodeChain_0[position] = curDevice.subCtrlList_0[i];
						}
						else
						{
							ControlNode firstNode = (ControlNode)(curDevice.ctrlNodeChain_0[position]);
							firstNode.brother.Add(curDevice.subCtrlList_0[i]);
							firstNode.brotherCount++;
							foreach (ControlNode b in firstNode.brother)
							{
								b.brotherCount = firstNode.brotherCount;
							}
						}
					}
					detectedSubNum = curDevice.subCtrlList_0.Count;

					// 设置附属设备在级联链中的位置
					for (int j = 0; j < curDevice.equipmentList_0.Count; j++)
					{
						int position = curDevice.equipmentList_0[j].myEquipmentDepth + 1000;
						if (!curDevice.ctrlNodeChain_0.Contains(position))
						{
							curDevice.ctrlNodeChain_0[position] = curDevice.equipmentList_0[j];
						}
						else
						{
							ControlNode firstNode = (ControlNode)(curDevice.ctrlNodeChain_0[position]);
							firstNode.brother.Add(curDevice.equipmentList_0[j]);
							firstNode.brotherCount++;
							foreach (ControlNode b in firstNode.brother)
							{
								b.brotherCount = firstNode.brotherCount;
							}
						}
					}
					detectedEquNum = curDevice.equipmentList_0.Count;

					// 判断分控编号是否连续
					foreach (int key in curDevice.ctrlNodeChain_0.Keys)
					{
						ControlNode node = ((ControlNode)curDevice.ctrlNodeChain_0[key]);
						// 分控
						if (node.type == 1)
						{
							int subCtrlDepth = node.mySubCtrlDepth;
							if (subCtrlDepth > 1 && !curDevice.ctrlNodeChain_0.ContainsKey(subCtrlDepth - 1))
							{
								node.noContinueFlag = true;
							}
						}
						// 附属设备
						else if (node.type == 0)
						{
							int equipmentDepth = node.myEquipmentDepth + 1000;
							if (equipmentDepth > 1000 && !curDevice.ctrlNodeChain_0.ContainsKey(equipmentDepth - 1))
							{
								node.noContinueFlag = true;
							}
						}
					}

					curDevice.totalCtrlNodeNum_0 = curDevice.ctrlNodeChain_0.Keys.Count;

					// 设置网口活跃状态
					if (curDevice.totalCtrlNodeNum_0 > 0)
					{
						curDevice.activeStatus = true;
					}
					else
					{
						curDevice.activeStatus = false;
					}

					if (topologyDetect_LoopNum % 600 == 0)
					{
						typeLog("Topology Detect: DeviceIndex = " + k + ", TotalSubNum = " + curDevice.totalCtrlNodeNum_0);
					}
				}
			}

			//Console.WriteLine("detectedDeviceNum = " + detectedDeviceNum);
			
			return new IntPair(detectedSubNum, detectedEquNum);
		}

        // 收集检测结果 - 线程方法 ArtNet方式
        private IntPair collectDetectResultsArt() {
            int detectedSubNum = 0;
            int detectedEquNum = 0;

            var dic = GetArtSubUnvUIDList();
            if (dic != null) {
                detectedSubNum = dic.Count;
            }
            return new IntPair(detectedSubNum, detectedEquNum);
        }

		#endregion

		#region 截屏与下发显示数据

        // 截屏下发 - 线程方法
		private void captureSendProcess()
		{
			int sleepTime = 0;
			int frameId = -1;
			lastStartTicksImage = 0;
			timeoutCount = 0;
			while (true)
			{
				sw_total.Restart();

				frameId++;
                currFrameTotal++;
				if (frameId > 1000000)
				{
					frameId = 0;
				}
                if (currFrameTotal > int.MaxValue - 100)
                {
                    currFrameTotal = 0;
                }
                if (captureSendState == CaptureSendState.Run)
                {
                    doOneCapture();
                }

				for (int k = 0; k < netDeviceList.Count; k++)
				{
					NetDevice dev = netDeviceList[k];

					// 当前网口活跃
					if (dev.activeStatus == true)
					{
						if (captureSendState == CaptureSendState.Pause)
						{
							// 暂停态：帧包 + 显示设置包（黑屏打开，锁屏打开）
							dev.packetSender.sendOneFramePacket();
							dev.packetSender.sendDispConfigPacket(ParaState.ON, ParaState.ON);
						}
						else if (captureSendState == CaptureSendState.Run)
						{
                            // 截屏发送态：帧包 + 显示设置包（黑屏关闭，锁屏关闭） + 显示数据包
                            long kkk=0, kkk1=0;
                            while (isSending) // 可能导致帧率降低
                            {
                                Thread.Sleep(1);
                                kkk1 = kkk;
                                kkk = sw_total.ElapsedMilliseconds;
                                if (kkk > 500) // 超过帧间隔则继续发包,可能导致画面闪烁
                                {
                                    break;
                                }
                            }
                            //typeLog("kkk=" + kkk  + ";   kkk1=" + kkk1, LogFile.Debug, LogTarget.FileOnly);

                            sendFrameThread(dev, frameId);
                            //doOneCaptureAndSend(dev,frameId);
						}
						else if (captureSendState == CaptureSendState.Stop)
						{
							// 停止态：帧包 + 显示设置包（黑屏打开，锁屏关闭）
							dev.packetSender.sendOneFramePacket();
							dev.packetSender.sendDispConfigPacket(ParaState.ON, ParaState.OFF);
						}
					}
					// 当前网口不活跃
					else
                    {
                        // 帧包 + 显示设置包（黑屏打开，锁屏关闭）
                        dev.packetSender.sendOneFramePacket();
                        dev.packetSender.sendDispConfigPacket(ParaState.ON, ParaState.OFF);
                    }
                }

				sw_total.Stop();

				// 计算线程应休眠的时间
				sleepTime = sleepTimeBase - (int)(sw_total.ElapsedMilliseconds);
				sleepTime = sleepTime > 0 ? sleepTime : 0;
				Thread.Sleep(sleepTime);
			}
		}

		// 截屏下发主逻辑动作 - [可选]带有异形映射逻辑
		private void doOneCaptureAndSend(NetDevice dev, int frameId)
		{
			// 生成异形地址映射表时, 不执行截屏下发操作
			if (addrMapIsBuilding == true)
			{
				return;
			}

			try
			{
				Bitmap bitmap = new Bitmap(captureSize.X, captureSize.Y);

				// 截屏
                var start = DateTime.Now;
				Graphics g = Graphics.FromImage(bitmap);
				g.CopyFromScreen(captureLocation.X, captureLocation.Y, 0, 0, new Size(captureSize.X, captureSize.Y));
				g.Dispose();
                var end = DateTime.Now;
                var msg = string.Format("copyScreen {0}ms", (end - start).TotalMilliseconds);
                //typeLog(msg, LogFile.Debug, LogTarget.FileOnly);

				BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, captureSize.X, captureSize.Y), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

				// 根据 LedDebug 设置的有效分控、有效端口进行数据筛选
				if (baseMapSrcType == MapSrcType.NoMap)
				{
					filtFrameData(bmpData);
				}

				// 根据异形映射表对帧数据进行异形映射变换
				mapFrameData(bmpData);

				bitmap.UnlockBits(bmpData);

				// 下发

#if false // 同步
                start = DateTime.Now;
                sendOneFrameFromBuffer(dev, frameBuffer_RGB, channelNumTotal, out_h, frameId);
                end = DateTime.Now;
                msg = string.Format("sendOneFrameFromBuffer {0}ms", (end - start).TotalMilliseconds);
                typeLog(msg, LogFile.Debug, LogTarget.FileOnly);
#else // 异步
                var sendThread = new Thread(() =>
                {
                    while (isSending) // 可能导致帧率降低
                    {
                        Thread.Sleep(1);
                        //if ((DateTime.Now.Ticks - lastStartTicksImage > sleepTimeBase * 10000) && (lastStartTicksImage != 0)) // 超过帧间隔则继续发包,可能导致画面闪烁
                        //{
                        //    break;
                        //}
                    }
                        var buffer = new byte[frameBuffer_RGB.Length];
                        Array.Copy(frameBuffer_RGB, buffer, buffer.Length);
                        var total = channelNumTotal;
                        var temp_out_h = out_h;
                        var tempId = frameId;
                        sendOneFrameFromBuffer(dev, buffer, total, temp_out_h, tempId);
                    });
                sendThread.IsBackground = true;
                sendThread.Start();
#endif

                bitmap.Dispose();
				bitmap = null;
			}
			catch (Exception ex)
			{
				if (showExceptionWhileCapture)
				{
					typeLog("================ Catched Exception ================");
					typeLog("ExceptionNum" + (++captureExceptionNum));
					typeLog(ex.Message + ex.StackTrace);
					typeLog("TargetSite.Name = " + ex.TargetSite.Name);
					typeLog("============== Catched Exception End ==============");
				}

				if (ex.TargetSite.Name.Equals("CopyFromScreen"))
				{
					return;
				}
				if (ex.TargetSite.Name.Equals("LockBits"))
				{
					return;
				}
			}
		}

        //截屏
        private void doOneCapture()
        {
            // 生成异形地址映射表时, 不执行截屏操作
            if (addrMapIsBuilding == true)
            {
                return;
            }

            try
            {
                Bitmap bitmap = new Bitmap(captureSize.X, captureSize.Y);

                // 截屏
                var start = DateTime.Now;
                Graphics g = Graphics.FromImage(bitmap);
                g.CopyFromScreen(captureLocation.X, captureLocation.Y, 0, 0, new Size(captureSize.X, captureSize.Y));
                g.Dispose();
                var end = DateTime.Now;
                var msg = string.Format("copyScreen {0}ms", (end - start).TotalMilliseconds);
                //typeLog(msg, LogFile.Debug, LogTarget.FileOnly);

                BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, captureSize.X, captureSize.Y), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

                // 根据 LedDebug 设置的有效分控、有效端口进行数据筛选
                if (baseMapSrcType == MapSrcType.NoMap)
                {
                    filtFrameData(bmpData);
                }

                // 根据异形映射表对帧数据进行异形映射变换
                mapFrameData(bmpData);

                bitmap.UnlockBits(bmpData);

                bitmap.Dispose();
                bitmap = null;
            }
            catch (Exception ex)
            {
                if (showExceptionWhileCapture)
                {
                    typeLog("================ Catched Exception ================");
                    typeLog("ExceptionNum" + (++captureExceptionNum));
                    typeLog(ex.Message + ex.StackTrace);
                    typeLog("TargetSite.Name = " + ex.TargetSite.Name);
                    typeLog("============== Catched Exception End ==============");
                }

                if (ex.TargetSite.Name.Equals("CopyFromScreen"))
                {
                    return;
                }
                if (ex.TargetSite.Name.Equals("LockBits"))
                {
                    return;
                }
            }
        }

        //发送一帧图像
        private void sendFrameThread(NetDevice dev, int frameId)
        {
#if false // 同步
            sendOneFrameFromBuffer(dev, frameBuffer_RGB, channelNumTotal, out_h, frameId);
#else // 异步
            var sendThread = new Thread(() =>
            {
                var buffer = new byte[frameBuffer_RGB.Length];
                Array.Copy(frameBuffer_RGB, buffer, buffer.Length);
                var total = channelNumTotal;
                var temp_out_h = out_h;
                var tempId = frameId;
                sendOneFrameFromBuffer(dev, buffer, total, temp_out_h, tempId);
            });
            sendThread.IsBackground = true;
            sendThread.Start();
#endif
        }

		// 向网口发送一帧
		private void sendOneFrameFromBuffer(NetDevice dev, byte[] buffer, int channelNumTotal, int height, int frameId = -1)
		{
			if ((frameId % 1000) == 0)
			{
				string logMessage = String.Format("Frame Id: {0,-6}    Time: {1,-20}    Tick: {2,-20}    TimeoutCount: {3,-4}", frameId, DateTime.Now, DateTime.Now.Ticks / 10000, timeoutCount);
				typeLog(logMessage, LogFile.Normal, LogTarget.FileOnly);
			}

			// 主逻辑动作
			DateTime startPoint = DateTime.Now;
            //lock (sendLocker) // 使用线程锁可能导致帧率降低
            {
                isSending = true;
                dev.packetSender.sendOneFrameFromBuffer(buffer, channelNumTotal, height, useSwitch);
                isSending = false;
            }
			DateTime endPoint = DateTime.Now;

			// 打印日志
			if (endPoint.Ticks - startPoint.Ticks > 100 * 10000)    // 发送超时
			{
				timeoutCount++;
				string logMessage = String.Format("Image Send Timeout   devIndex = {0,-4} frameId = {1,-10}{2,-25}{3,-25}{4,-10}{5,-10}", dev.index, frameId, startPoint.Ticks / 10000, endPoint.Ticks / 10000, (endPoint.Ticks - startPoint.Ticks) / 10000, (startPoint.Ticks - lastStartTicksImage) / 10000);
				//typeLog(logMessage, LogFile.Normal, LogTarget.Both);
			}
			else if ((startPoint.Ticks - lastStartTicksImage > 100 * 10000) && (lastStartTicksImage != 0))   // 间隔超时
			{
				timeoutCount++;
				if (timeoutCount > 1)
				{
					string logMessage = String.Format("Image Interval Timeout   devIndex = {0,-4} frameId = {1,-10}{2,-25}{3,-25}{4,-10}{5,-10}", dev.index, frameId, startPoint.Ticks / 10000, endPoint.Ticks / 10000, (endPoint.Ticks - startPoint.Ticks) / 10000, (startPoint.Ticks - lastStartTicksImage) / 10000);
					//typeLog(logMessage, LogFile.Normal, LogTarget.Both);
				}
			}
			else
			{
				string logMessage = String.Format("Image OK      devIndex = {0,-4} frameId = {1,-10}{2,-25}{3,-25}{4,-10}{5,-10}", dev.index, frameId, startPoint.Ticks / 10000, endPoint.Ticks / 10000, (endPoint.Ticks - startPoint.Ticks) / 10000, (startPoint.Ticks - lastStartTicksImage) / 10000);
				typeLog(logMessage, LogFile.Test, LogTarget.FileOnly);
			}

			lastStartTicksImage = startPoint.Ticks;
			if (timeoutCount > 1000000)
			{
				timeoutCount = 0;
			}
		}

        // 抓取时间定时器动作
        private void frameCountTimer_Ticked()
        {
            while (isFrameCountTimerRunning)
            {
                // 统计帧率
                var fps = currFrameTotal - lastFrameCount;
                lastFrameCount = currFrameTotal;
                var msg = string.Format("FPS:{0}", fps);
                onUpdateStatusStr(msg);

                Thread.Sleep(1000);
            }
        }

        public void StartFrameCountTimer() {
            if (isFrameCountTimerRunning) {
                return;
            }
            isFrameCountTimerRunning = true;
            var t = new Thread(frameCountTimer_Ticked);
            t.IsBackground = true;
            t.Start();
        }

        public void StopFrameCountTimer() {
            isFrameCountTimerRunning = false;
        }

		#endregion

		#region 读写分控及附属设备

		// 检查参数合法性
		private bool checkParaments(int devIndex, int nodeIndex, uint memAddr, byte[] memData, string nodeType)
		{
			if (devIndex >= netDeviceList.Count)
			{
				return false;
			}
			else if (devIndex < 0)
			{
				return false;
			}

			if (nodeType.Equals(NodeType.SubCtrl))
			{
				if (nodeIndex < 0 && nodeIndex > 255)
				{
					return false;
				}
			}

			if (nodeType.Equals(NodeType.Equipment))
			{
				if (nodeIndex < 0 && nodeIndex > 63)
				{
					return false;
				}
			}

			if (memData == null)
			{
				return false;
			}

			return true;
		}

		// 读取分控固件区进度事件处理
		private void readFlashStateUpdated(object source, ReadEventArgs e)
		{
			readFlashProcessValue = e.processValue;
			ReadEventArgs readArgs = new ReadEventArgs();
			readArgs.processValue = readFlashProcessValue;
			ReadFlashStateUpdated(this, readArgs);
		}

		#endregion

		#region 支持多路交换机

		// 根据 idMacList 生成"行映射表"、"分控映射表"、"附属设备映射表"
		private bool makeMacTable()
		{
			try
			{
				if (idMacList != null && idMacList.Count > 0)
				{
					// 生成 "行映射表"
					rowMacTable.Clear();
					int currentOffset = 0;
					foreach (IdMacPair idMacPair in idMacList)
					{
						int startId = idMacPair.startId;
						int endId = idMacPair.endId;
						for (int i = ((startId - 1) * 8); i <= ((endId * 8) - 1); i++)
						{
							MacTable macTable = new MacTable();
							macTable.id = i;
							macTable.macIndex = idMacPair.mac;
							macTable.offset = currentOffset;
							rowMacTable.Add(macTable);
						}
						currentOffset += (endId - startId + 1) * 8;
					}

					// 生成 "分控映射表"
					subMacTable.Clear();
					currentOffset = 0;
					foreach (IdMacPair idMacPair in idMacList)
					{
						int startId = idMacPair.startId;
						int endId = idMacPair.endId;
						for (int i = startId; i <= endId; i++)
						{
							MacTable macTable = new MacTable();
							macTable.id = i;
							macTable.macIndex = idMacPair.mac;
							macTable.offset = currentOffset;
							subMacTable.Add(macTable);
						}
						currentOffset += (endId - startId + 1);
					}

					// 生成 "附属设备映射表" - 暂时将所有附属设备映射到第一个端口（默认）
					equMacTable.Clear();
					currentOffset = 0;
					string firstPortMac = idMacList[0].mac;
					foreach (IdMacPair idMacPair in idMacList)
					{
						int startId = idMacPair.startId;
						int endId = idMacPair.endId;
						for (int i = startId; i <= endId; i++)
						{
							MacTable macTable = new MacTable();
							macTable.id = i;
							macTable.macIndex = firstPortMac;
							macTable.offset = currentOffset;
							equMacTable.Add(macTable);
						}
						currentOffset += (endId - startId + 1);
					}

					return true;
				}
				else
				{
					return false;
				}
			}
			catch (Exception e)
			{
				typeLog("[Error] " + e.Message);
				return false;
			}
		}

		#endregion

		#region 异形映射

		// 生成地址映射表 - 执行时机: 初始化时, 采集分辨率变化时, 重新刷新映射表时
		private void buildAddrMapTable()
		{
			// 设置映射表重计算标志
			addrMapIsBuilding = true;

			switch (baseMapSrcType)		// addrMapSrcType: 异形映射数据来源-{无/lwf/json}
			{
				case MapSrcType.UseBin:
					buildAddrMapTableUseBin(baseMapMode, baseColorString);
					break;
				case MapSrcType.UseJson:
					buildAddrMapTableUseJson(baseMapMode, baseColorString);
					break;
				default:
					out_l = captureSize.X;
					out_h = captureSize.Y;
                    buildAddrMapTableDefault(baseMapMode, baseColorString);
					break;
			}

			// 清除映射表重计算标志
			addrMapIsBuilding = false;
		}

		// 生成地址映射表 - 执行时机: 更改异形使能时, 更改映射文件时, 重新刷新映射表时
		private void buildAddrMapTable(MapSrcType mapSrcType)
		{
			// 设置映射表重计算标志
			addrMapIsBuilding = true;

			switch (mapSrcType)
			{
				case MapSrcType.UseBin:
					baseMapSrcType = MapSrcType.UseBin;
					buildAddrMapTableUseBin(baseMapMode, baseColorString);
					Console.ForegroundColor = ConsoleColor.DarkYellow;
					typeLog(string.Format("[BaseCoreNet] buildAddrMapTable, addrMapSrcType = UseBin, lwfHasPortInfoData = {0}, lwfHasChannelInfoData = {1}", lwfHasPortInfoData, lwfHasChannelInfoData));
					Console.ResetColor();
					break;
				case MapSrcType.UseJson:
					baseMapSrcType = MapSrcType.UseJson;
					buildAddrMapTableUseJson(baseMapMode, baseColorString);
					Console.ForegroundColor = ConsoleColor.DarkYellow;
					typeLog("[BaseCoreNet] buildAddrMapTable, addrMapSrcType = UseJson");
					Console.ResetColor();
					break;
				default:
					baseMapSrcType = MapSrcType.NoMap;
					out_l = captureSize.X;
					out_h = captureSize.Y;
                    buildAddrMapTableDefault(baseMapMode, baseColorString);
					Console.ForegroundColor = ConsoleColor.DarkYellow;
					typeLog("[BaseCoreNet] buildAddrMapTable, addrMapSrcType = NoMap", LogFile.Normal, LogTarget.Both);
					Console.ResetColor();
					break;
			}

			// 清除映射表重计算标志
			addrMapIsBuilding = false;
		}

        // 生成地址映射表的实际动作 - 不使用异形
        private void buildAddrMapTableDefault(MapMode mapMode, string colorString) {
            DispConfigPacketPara d = getDispConfigPacketPara();
            d.ChannelMapEnable = ParaState.OFF;	// 关闭通道映射使能
            setDispConfigPacketPara(d);

            Bitmap tmpBmp = new Bitmap(captureSize.X, captureSize.Y);
            BitmapData bmpData = tmpBmp.LockBits(new Rectangle(0, 0, captureSize.X, captureSize.Y), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

            // 生成异形映射表
            buildAddrMapData(MapSrcType.NoMap, mapMode, colorString, bmpData.Stride, bmpData.Stride * bmpData.Height);

            tmpBmp.Dispose();
        }

		// 生成地址映射表的实际动作 - 使用 lwf 文件中的 bin 数据, 或从 dxf 解析出的 bin 数据
		private void buildAddrMapTableUseBin(MapMode mapMode, string colorString)
		{
			if (binData == null)		// lwf中不存在异形数据（Bin-1）
			{
				baseMapSrcType = MapSrcType.NoMap;
				DispConfigPacketPara d = getDispConfigPacketPara();
				d.ChannelMapEnable = ParaState.OFF;	// 关闭通道映射使能
				setDispConfigPacketPara(d);
				return;
			}

			// 启用异形时
			out_h = subNumInBin1 * 8;		// 输出的行数 = 分控数 * 8
			out_l = maxLampNumInBin + 8;	// 输出的列数 = lwf中所有端口的最大带灯数 + 8

			if (binDataChannel == null)	// lwf中不存在通道映射数据（Bin-2）
			{
				DispConfigPacketPara d = getDispConfigPacketPara();
				d.ChannelMapEnable = ParaState.OFF;	// 关闭通道映射使能
				setDispConfigPacketPara(d);
			}
			else
			{
				DispConfigPacketPara d = getDispConfigPacketPara();
				d.ChannelMapEnable = ParaState.ON;	// 开启通道映射使能
				setDispConfigPacketPara(d);
			}

			Bitmap tmpBmp = new Bitmap(captureSize.X, captureSize.Y);
			BitmapData bmpData = tmpBmp.LockBits(new Rectangle(0, 0, captureSize.X, captureSize.Y), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

			// 生成异形映射表
			buildAddrMapData(MapSrcType.UseBin, mapMode, colorString, bmpData.Stride, bmpData.Stride * bmpData.Height);

			tmpBmp.Dispose();
		}

		// 【停用】生成地址映射表的实际动作 - 使用 json 解析的数据
		private void buildAddrMapTableUseJson(MapMode mapMode, string colorString)
		{
			if (addrMapJson == null || addrMapJson.subInfoList.Count == 0)	// json 中不存在异形数据
			{
				baseMapSrcType = MapSrcType.NoMap;
				return;
			}

			// 启用异形时
			out_h = addrMapJson.subNum * 8;										// 输出的行数 = 分控数 * 8
			out_l = ((int)(addrMapJson.maxLampNum_TotalPort / 12)) * 12 + 12;	// 输出的列数 = lwf中所有端口的最大带灯数 + 8
			if (out_l < 96)
			{
				out_l = 96;
			}
			out_pixel = out_l * 3 / 4;

			Bitmap tmpBmp = new Bitmap(captureSize.X, captureSize.Y);
			BitmapData bmpData = tmpBmp.LockBits(new Rectangle(0, 0, captureSize.X, captureSize.Y), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

			// 生成异形映射表
			buildAddrMapData(MapSrcType.UseJson, mapMode, colorString, bmpData.Stride, bmpData.Stride * bmpData.Height);
		}

		// 根据异形映射模式生成异形映射表 addrMapType[], addrMap[], addrMapOffset[]
		private void buildAddrMapData(MapSrcType mapSrcType, MapMode mapMode, string colorString, int bmpStride, int bmpDataLength)
		{
			MapMode currentMapMode = mapMode;

			#region 查找异形最大范围 无数据源时不查找
			
			int sss, ppp, bbb, bbb_addr, temp_x, temp_y, max_x, max_y;
            double scale_x = 1;
            double scale_y = 1;

			max_x = 0;
			max_y = 0;

            if (mapSrcType != MapSrcType.NoMap) {
                if (lwfSystemArea.X != -1 && lwfSystemArea.Y != -1) {
                    max_x = lwfSystemArea.X - 1;
                    max_y = lwfSystemArea.Y - 1;
                } else {
                    for (int y = 0; y < out_h; y++) {
                        // 计算行 y 对应的控制器 subNum 和端口 portNum
                        sss = y >> 3;
                        ppp = y & 0x07;

                        // 计算在内部异形格式（bin）中的该行的行相对偏移
                        bbb = (sss + 1) * 0x8000 + ppp * 4;

                        for (int x = 0; x < out_l; x++) {
                            bbb_addr = bbb + x * 32;

                            // 获取灯具的映射坐标
                            temp_x = binData[bbb_addr + 2] * 256 + binData[bbb_addr + 3];
                            temp_y = binData[bbb_addr] * 256 + binData[bbb_addr + 1];

                            // 无效点不参与 max_x、max_y 计算
                            if (temp_x == 0xffff && temp_y == 0xffff) {
                                continue;
                            }

                            if (temp_x > max_x) {
                                max_x = temp_x;
                            }
                            if (temp_y > max_y) {
                                max_y = temp_y;
                            }
                        }
                    }
                }

                scale_x = captureSize.X - 1;
                scale_y = captureSize.Y - 1;

                if (max_x > 0) {
                    scale_x = scale_x / max_x;
                } else {
                    scale_x = 1;
                }

                if (max_y > 0) {
                    scale_y = scale_y / max_y;
                } else {
                    scale_y = 1;
                }
            }

			#endregion

            #region 无数据源
            if (mapSrcType == MapSrcType.NoMap) {
                // 判断单个灯点通道数
                var channelPerPixel = 3;
                if (!string.IsNullOrEmpty(colorString)) {
                    channelPerPixel = colorString.Length;
                }

                var pixelNumTotal = out_h * out_l;
                channelNumTotal = pixelNumTotal * channelPerPixel;
                // 初始化异形映射表
                addrMap = new int[channelNumTotal];
                addrMapType = new int[channelNumTotal];
                addrMapOffset = new int[channelNumTotal];

                for (int i = 0; i < pixelNumTotal; i++) {
                    for (var j = 0; j < channelPerPixel; j++) {
                        var chan = i * channelPerPixel + j; // 实际通道位置
                        var pixelAddr = i * 3;
                        if (pixelAddr > bmpDataLength) {
                            pixelAddr = 0;
                        }
                        addrMap[chan] = pixelAddr;
                        addrMapOffset[chan] = getColorOffsetEx(colorString, j);
                        addrMapType[chan] = userColorType;
                    }
                }

                
            }
            #endregion

            #region bin 数据源

            else if (mapSrcType == MapSrcType.UseBin)
			{
				int addr = 0;
				int pixelAddr = 0;
				int bin_offset = 0;
				int bin_addr = 0;
				int subNum, portNum, map_x, map_y;

				// lwf 文件中有通道映射信息
				if (lwfHasChannelInfoData == true)
				{
					currentMapMode = MapMode.CustomByChannel;
				}
				// lwf 文件中无通道映射信息, 但有端口映射信息
				else if (lwfHasPortInfoData == true)
				{
					currentMapMode = MapMode.CustomByPort;
				}
				// lwf 文件中无通道映射信息, 无端口映射信息, 按照界面的配置（mapMode）确定颜色映射模式
				else
				{
					ColorMode colorMode = new ColorMode();
					if (mapMode == MapMode.Baned)				// 禁用模式
					{
						colorMode.addrMapType = 0;
					}
					else if (mapMode == MapMode.FourPure)		// 四色纯模式
					{
						colorMode.addrMapType = 1;
						colorMode.colorString = colorString;
					}
					else if (mapMode == MapMode.FourIncrease)	// 四色增强模式
					{
						colorMode.addrMapType = 2;
						colorMode.colorString = colorString;
                    }
                    colorMode.colorArray = new int[4];
                    colorMode.colorArray[0] = getColorOffset(colorString, 0);
                    colorMode.colorArray[1] = getColorOffset(colorString, 1);
                    colorMode.colorArray[2] = getColorOffset(colorString, 2);
                    colorMode.colorArray[3] = getColorOffset(colorString, 3);

					portColorModeList.Clear();
					portColorModeList.Add(colorMode);

					currentMapMode = MapMode.CustomByPara;
				}

				// 计算总通道数 channelNumTotal
				if (currentMapMode == MapMode.Baned)	// 三色模式
				{
					channelNumTotal = out_h * out_l * 3;
				}
				else if (currentMapMode == MapMode.FourPure || currentMapMode == MapMode.FourIncrease)	// 四色模式
				{
					channelNumTotal = out_h * out_l * 4;
				}
				else if (currentMapMode == MapMode.CustomByPort)	// 自定义模式, 由 lwf 文件中的端口映射指定
				{
					channelNumTotal = 0;

					// 循环行
					for (int y = 0; y < out_h; y++)
					{
						// 计算行 y 对应的控制器 subNum 和端口 portNum
						subNum = y >> 3;
						portNum = y & 0x07;
						int colorModeIndex = subNum * 8 + portNum;
						channelNumTotal += (out_l * portColorModeList[colorModeIndex].colorArray.Length);	// 端口为同构灯具
					}
				}
				else if (currentMapMode == MapMode.CustomByPara)	// 自定义模式, 由软件内部变量指定（分控设置-灯具类型）
				{
					channelNumTotal = 0;

					// 循环行
					for (int y = 0; y < out_h; y++)
					{
						channelNumTotal += (out_l * portColorModeList[0].colorArray.Length);	// 端口为同构灯具
					}
				}
				else if (currentMapMode == MapMode.CustomByChannel)	// 自定义模式, 由 lwf 文件中的通道映射指定
				{
					channelNumTotal = 0;
                    portChannelMax = 0;
 					subNumInBin2 = binDataChannel[0x0100] << 8 | binDataChannel[0x0101];
					for (int iSub = 0; iSub < subNumInBin2; iSub++)
					{
						for (int iPort = 0; iPort < 8; iPort++)
						{
							int baseAddr = 0x01000 + 128 * iSub + 8 * iPort + 0x0C;
							int currentPortChannelNum = binDataChannel[baseAddr + 0] << 24 | binDataChannel[baseAddr + 1] << 16 | binDataChannel[baseAddr + 2] << 8 | binDataChannel[baseAddr + 3];
							if (currentPortChannelNum > portChannelMax)
							{
								portChannelMax = currentPortChannelNum;
							}
						}
					}
                    portChannelMax = portChannelMax + 24;
                    channelNumTotal = subNumInBin2 * 8 * portChannelMax;
				}

				// 初始化异形映射表
				addrMap = new int[channelNumTotal];
				addrMapType = new int[channelNumTotal];
				addrMapOffset = new int[channelNumTotal];
				addrMapPortTag = new int[channelNumTotal];

				// 异形映射表赋值 - 按通道映射
				if (currentMapMode == MapMode.CustomByChannel)
				{
					int currentChannel = 0;		// 已处理的通道数

                    for (int i = 0; i < channelNumTotal; i++)
                    {
                        addrMap[i] = 0;
                        addrMapType[i] = 3;		// 填充为高亮增强模式2
                        addrMapOffset[i] = 0;
						addrMapPortTag[i] = 0;
                    }
					
					subNumInBin2 = binDataChannel[0x0100] << 8 | binDataChannel[0x0101];
					for (int iSub = 0; iSub < subNumInBin2; iSub++)
					{
						for (int iPort = 0; iPort < 8; iPort++)
						{
							int baseAddr = 0x01000 + 128 * iSub + 12 + iPort * 8;
							int portChannelNum = binDataChannel[baseAddr + 0] << 24 | binDataChannel[baseAddr + 1] << 16 | binDataChannel[baseAddr + 2] << 8 | binDataChannel[baseAddr + 3];
							int portChannelAddr = binDataChannel[baseAddr + 4] << 24 | binDataChannel[baseAddr + 5] << 16 | binDataChannel[baseAddr + 6] << 8 | binDataChannel[baseAddr + 7];
							for (int iChannel = 0; iChannel < portChannelNum; iChannel++)
							{
								int baseAddrTube = portChannelAddr + iChannel * 8;
                                currentChannel = (iSub * 8 + iPort) * portChannelMax + iChannel;

								// 获取灯具的映射坐标
								map_x = binDataChannel[baseAddrTube + 2] << 8 | binDataChannel[baseAddrTube + 3];
								map_y = binDataChannel[baseAddrTube + 0] << 8 | binDataChannel[baseAddrTube + 1];

								if (map_x != 0xffff && map_y != 0xffff)
								{
									map_x = (int)(map_x * scale_x);
									map_y = (int)(map_y * scale_y);

									// 计算该坐标在 bmpData 中的地址（像素首地址）
									pixelAddr = map_y * bmpStride + map_x * 3;
									if (pixelAddr > bmpDataLength)
									{
										pixelAddr = 0;
									}
								}
								else
								{
									pixelAddr = 0;
								}

								// 获取通道颜色
								int channelColor = binDataChannel[baseAddrTube + 4];

								// 填充 addrMap[]
								addrMap[currentChannel] = pixelAddr;	// 统一填充像素首地址

								// 填充 addrMapOffset[]
								if (channelColor < 3)
								{
									addrMapOffset[currentChannel] = 2 - channelColor;	// bmp为 BGR 顺序存储，需调整为 RGB 顺序
								}
								else
								{
									addrMapOffset[currentChannel] = channelColor;
								}

								// 填充 addrMapType[]
                                addrMapType[currentChannel] = binDataChannel[baseAddrTube + 5]; ;

								// 填充 addrMapPortTag[]
								addrMapPortTag[currentChannel] = iSub * 8 + iPort + 1;
                            }
						}
					}
                    currentChannel = 0;
				}
				else
				{
					// 循环行
					for (int y = 0; y < out_h; y++)
					{
						// 计算行 y 对应的控制器 subNum 和端口 portNum
						subNum = y >> 3;
						portNum = y & 0x07;

						int colorModeIndex = subNum * 8 + portNum;
						if (lwfHasPortInfoData == false)
						{
							colorModeIndex = 0;
						}

						// 计算在内部异形格式（bin）中的该行的行相对偏移
						bin_offset = (subNum + 1) * 0x8000 + portNum * 4;

						/*****************************************************************************/

						ColorMode currentColorMode = portColorModeList[colorModeIndex];

						for (int x = 0; x < out_l; x++)
						{
							// 计算灯具在内部异形格式（bin）中的地址
							bin_addr = bin_offset + x * 32;

							// 获取灯具的映射坐标
							map_x = binData[bin_addr + 2] << 8 | binData[bin_addr + 3];
							map_y = binData[bin_addr] << 8 | binData[bin_addr + 1];

							if (map_x != 0xffff && map_y != 0xffff)
							{
								map_x = (int)(map_x * scale_x);
								map_y = (int)(map_y * scale_y);

								// 计算该坐标在 bmpData 中的地址（像素首地址）
								pixelAddr = map_y * bmpStride + map_x * 3;
								if (pixelAddr > bmpDataLength)
								{
									pixelAddr = 0;
								}
							}
							else
							{
								pixelAddr = 0;
							}

							for (int c = 0; c < currentColorMode.colorArray.Length; c++)
							{
								// 填充 addrMap[]
								addrMap[addr + c] = pixelAddr;				// 统一填充像素首地址

								// 填充 addrMapOffset[]
								if (currentColorMode.colorArray[c] < 3)
								{
									addrMapOffset[addr + c] = 2 - c + currentColorMode.colorArray[c];
								}
								else
								{
									addrMapOffset[addr + c] = currentColorMode.colorArray[c];
								}

								// 填充 addrMapType[]
								addrMapType[addr + c] = currentColorMode.addrMapType;

								// 填充 addrMapPortTag[]
								addrMapPortTag[addr + c] = subNum * 8 + portNum + 1;
							}

							// 计算下一次填充的起始地址
							addr += currentColorMode.colorArray.Length;
						}
					}
				}
			}

			#endregion

			#region Json 数据源 - 停用

			else if (mapSrcType == MapSrcType.UseJson)
			{
				// 逐点映射模式
				if (mapMode == MapMode.EachPoint)
				{
					channelNumTotal = 0;
					foreach (AddrMapJsonUnit mapUnit in addrMapJson.map)
					{
						channelNumTotal += mapUnit.type.Length;
					}

					addrMap = new int[channelNumTotal];
					addrMapType = new int[channelNumTotal];
					addrMapOffset = new int[channelNumTotal];

					int addr = 0;
					int offset = 0;
					// 根据 json 数据设置映射表
					foreach (AddrMapJsonUnit mapUnit in addrMapJson.map)
					{
						addr = (((int.Parse(mapUnit.Sub) - 1) * 8 + int.Parse(mapUnit.port) - 1) * out_pixel + int.Parse(mapUnit.lamp) - 1) * channelNum;
						offset = (int.Parse(mapUnit.y) - 1) * bmpStride + (int.Parse(mapUnit.x) - 1) * 3;
						if (offset > bmpDataLength)
						{
							offset = 0;
						}

						switch (mapUnit.type)
						{
							case "WBGR":
								addrMap[addr] = offset;
								addrMap[addr + 1] = offset;
								addrMap[addr + 2] = offset;
								addrMap[addr + 3] = offset;
								addrMapType[addr] = 3;
								addrMapType[addr + 1] = 2;
								addrMapType[addr + 2] = 1;
								addrMapType[addr + 3] = 0;
								break;
							case "RWGB":
								addrMap[addr] = offset;
								addrMap[addr + 1] = offset;
								addrMap[addr + 2] = offset;
								addrMap[addr + 3] = offset;
								addrMapType[addr] = 0;
								addrMapType[addr + 1] = 3;
								addrMapType[addr + 2] = 1;
								addrMapType[addr + 3] = 2;
								break;
							default://"RGBW"
								addrMap[addr] = offset;
								addrMap[addr + 1] = offset;
								addrMap[addr + 2] = offset;
								addrMap[addr + 3] = offset;
								addrMapType[addr] = 0;
								addrMapType[addr + 1] = 1;
								addrMapType[addr + 2] = 2;
								addrMapType[addr + 3] = 3;
								break;
						}
					}
				}
			}

			#endregion
		}

		// 根据异形映射表对帧数据进行异形映射变换 - unsafe
		private void mapFrameData(BitmapData bmpData)
		{
			if (frameBuffer_RGB == null || frameBuffer_RGB.Length != channelNumTotal)
			{
				frameBuffer_RGB = new byte[channelNumTotal];
			}

			unsafe
			{
#if false // 合并逻辑
				// 关闭异形映射时
				if (baseMapSrcType == MapSrcType.NoMap)
				{
                    if (!AllChannelSet)
                    {
                        for (int j = 0; j < captureSize.Y; j++)
                        {
                            byte* pp = (byte*)bmpData.Scan0 + j * bmpData.Stride;
                            for (int i = 0; i < captureSize.X; i++)
                            {
                                
                                var offset = (j * captureSize.X + i) * 3;
                                int graylvl = max(*pp, *(pp + 1), *(pp + 2));
                                if (graylvl > lowestGray || lowestGray == 0)
                                {
                                    frameBuffer_RGB[offset] = *(pp + 2);
                                    frameBuffer_RGB[offset + 1] = *(pp + 1);
                                    frameBuffer_RGB[offset + 2] = *pp;
                                }
                                else
                                {
                                    frameBuffer_RGB[offset] = 0;
                                    frameBuffer_RGB[offset + 1] = 0;
                                    frameBuffer_RGB[offset + 2] = 0;
                                }
                                pp += 3;
                            }
                        }
                    }
                    else
                    {
                        for (int j = 0; j < captureSize.Y; j++)
                        {
                            byte* pp = (byte*)bmpData.Scan0 + j * bmpData.Stride;
                            int graylvl = max(*pp, *(pp + 1), *(pp + 2));
                            for (int i = 0; i < captureSize.X; i++)
                            {
                                var offset = (j * captureSize.X + i) * 4;
                                if (graylvl > lowestGray || lowestGray == 0)
                                {
                                    int k = userColorType;
                                    switch (k)
                                    {
                                        case 0:     // 禁用
                                            frameBuffer_RGB[offset] = *(pp + 2);
                                            frameBuffer_RGB[offset + 1] = *(pp + 1);
                                            frameBuffer_RGB[offset + 2] = *pp;
                                            frameBuffer_RGB[offset + 3] = 0;
                                            break;
                                        case 1:     // 四色纯
                                            if ((*pp == *(pp + 1)) && (*(pp + 1) == *(pp + 2)))
                                            {
                                                frameBuffer_RGB[offset] = 0;
                                                frameBuffer_RGB[offset + 1] = 0;
                                                frameBuffer_RGB[offset + 2] = 0;
                                                frameBuffer_RGB[offset + 3] = *pp;
                                            }
                                            else
                                            {
                                                frameBuffer_RGB[offset] = *(pp + 2);
                                                frameBuffer_RGB[offset + 1] = *(pp + 1);
                                                frameBuffer_RGB[offset + 2] = *pp;
                                                frameBuffer_RGB[offset + 3] = 0;
                                            }
                                            break;
                                        case 2:     // 四色增强
                                            frameBuffer_RGB[offset] = *(pp + 2);
                                            frameBuffer_RGB[offset + 1] = *(pp + 1);
                                            frameBuffer_RGB[offset + 2] = *pp;
                                            frameBuffer_RGB[offset + 3] = (byte)(*pp * 0.1 + *(pp + 1) * 0.6 + (*pp + 2) * 0.3);
                                            break;
                                        case 3:     // 四色增强2  基本模式1 - W取RGB最小值
                                            frameBuffer_RGB[offset] = *(pp + 2);
                                            frameBuffer_RGB[offset + 1] = *(pp + 1);
                                            frameBuffer_RGB[offset + 2] = *pp;
                                            frameBuffer_RGB[offset + 3] = min(*pp, *(pp + 1), *(pp + 2));
                                            break;
                                        case 4:     // 白光动画模式 - 仅W点亮，三色稍暗，白色最亮
                                            frameBuffer_RGB[offset] = (byte)(*pp * 0.1 + *(pp + 1) * 0.6 + (*pp + 2) * 0.3);
                                            frameBuffer_RGB[offset + 1] = (byte)(*pp * 0.1 + *(pp + 1) * 0.6 + (*pp + 2) * 0.3);
                                            frameBuffer_RGB[offset + 2] = (byte)(*pp * 0.1 + *(pp + 1) * 0.6 + (*pp + 2) * 0.3);
                                            frameBuffer_RGB[offset + 3] = (byte)(*pp * 0.1 + *(pp + 1) * 0.6 + (*pp + 2) * 0.3);
                                            break;
                                        case 5:     // 任性模式 - W为RGB最低值，RGB各减去W值
                                            byte wcolor = min(*pp, *(pp + 1), *(pp + 2));
                                            frameBuffer_RGB[offset] = (byte)(*(pp + 2) - wcolor);
                                            frameBuffer_RGB[offset + 1] = (byte)(*(pp + 1) - wcolor);
                                            frameBuffer_RGB[offset + 2] = (byte)(*pp - wcolor);
                                            frameBuffer_RGB[offset + 3] = wcolor;
                                            break;
                                        case 6:     // 黄光增强 - R=G时W=R=G,否则W=0
                                            frameBuffer_RGB[offset] = *(pp + 2);
                                            frameBuffer_RGB[offset + 1] = *(pp + 1);
                                            frameBuffer_RGB[offset + 2] = *pp;
                                            if (*(pp + 2) == *(pp + 1))
                                            {
                                                frameBuffer_RGB[offset + 3] = *(pp + 2);
                                            }
                                            else
                                            {
                                                frameBuffer_RGB[offset + 3] = 0;
                                            }
                                            break;
                                        case 7:     // 白光增强 - W为固定值
                                            frameBuffer_RGB[offset] = *(pp + 2);
                                            frameBuffer_RGB[offset + 1] = *(pp + 1);
                                            frameBuffer_RGB[offset + 2] = *pp;
                                            frameBuffer_RGB[offset + 3] = 255;
                                            break;
                                        case 254:
                                            frameBuffer_RGB[offset] = *(pp + 2);
                                            frameBuffer_RGB[offset + 1] = *(pp + 1);
                                            frameBuffer_RGB[offset + 2] = *pp;
                                            frameBuffer_RGB[offset + 3] = (byte)userChannelLvl;
                                            break;
                                        default:
                                            break;
                                    }
                                }
                                else
                                {
                                    frameBuffer_RGB[offset] = 0;
                                    frameBuffer_RGB[offset + 1] = 0;
                                    frameBuffer_RGB[offset + 2] = 0;
                                    frameBuffer_RGB[offset + 3] = 0;
                                }
                                pp += 3;
                            }
                            
                        }
                    }
                    return;
				}
#endif

				if (addrMap.Length == frameBuffer_RGB.Length)
				{
					// LedDebug 指定的信息计算有效端口
					int effectPortTagStart = 0;
					int effectPortTagEnd = 0;
					if (effectLedDebugSub == 0 && effectLedDebugPort == 0)
					{
						effectPortTagStart = 1;
						effectPortTagEnd = int.MaxValue;
					}
					else
					{
						effectPortTagStart = (effectLedDebugSub - 1) * 8 + effectLedDebugPort;
						if (effectLedDebugPort == 0)
						{
							// LedDebug 指定的有效分控的 8 个端口均有效
							effectPortTagStart = effectPortTagStart + 1;
							effectPortTagEnd = effectPortTagStart + 7;
						}
						else
						{
							// LedDebug 指定的有效分控的 1 个端口有效
							effectPortTagEnd = effectPortTagStart;
						}
					}

					// 抽取异形映射显示数据
					byte* p = (byte*)bmpData.Scan0;
					for (int i = 0; i < addrMap.Length; i++)
					{
						int k = addrMapType[i];
                        if ((k == 3) && (userColorType != 255))
                        {
                            k = userColorType;
                        }

						// 按照 LedDebug 设置的有效分控、有效端口过滤数据
                        if (addrMapPortTag != null) {
                            if (addrMapPortTag[i] == 0 || addrMapPortTag[i] < effectPortTagStart || addrMapPortTag[i] > effectPortTagEnd) {
                                frameBuffer_RGB[i] = 0;
                                continue;
                            }
                        }

                        int graylvl = max(*(p + addrMap[i]), *(p + addrMap[i] + 1), *(p + addrMap[i] + 2));

						if (addrMapOffset[i] == 3)   // "N" 类型
						{
							frameBuffer_RGB[i] = 0;
						}
                        else if(graylvl <= lowestGray && lowestGray != 0)
                        {
                            frameBuffer_RGB[i] = 0;
                        }
						else
						{
							switch (k)
							{
								case 1:     // 四色纯
									if (((*(p + addrMap[i])) == (*(p + addrMap[i] + 1)) && ((*(p + addrMap[i])) == (*(p + addrMap[i] + 2)))))
									{
										if (addrMapOffset[i] < 3)
										{
											frameBuffer_RGB[i] = 0;
										}
										else
										{
											frameBuffer_RGB[i] = *(p + addrMap[i]);
										}
									}
									else
									{
										if (addrMapOffset[i] < 3)
										{
											frameBuffer_RGB[i] = *(p + addrMap[i] + addrMapOffset[i]);
										}
										else
										{
											frameBuffer_RGB[i] = 0;
										}
									}
									break;
								case 2:     // 四色增强
									if (addrMapOffset[i] > 3)
									{
										frameBuffer_RGB[i] = (byte)((*(p + addrMap[i]) * 0.1) + (*(p + addrMap[i] + 1) * 0.6) + (*(p + addrMap[i] + 2) * 0.3));
									}
									else
									{
										frameBuffer_RGB[i] = (byte)(*(p + addrMap[i] + addrMapOffset[i]));
									}
									break;
								case 3:     // 四色增强2  基本模式1 - W取RGB最小值
									if (addrMapOffset[i] > 3)
									{
										frameBuffer_RGB[i] = min(*(p + addrMap[i]), *(p + addrMap[i] + 1), *(p + addrMap[i] + 2));
									}
									else
									{
										frameBuffer_RGB[i] = (byte)(*(p + addrMap[i] + addrMapOffset[i]));
									}
									break;
                                case 4:     // 白光动画模式 - 仅W点亮，三色稍暗，白色最亮
                                    if (addrMapOffset[i] > 3)
                                    {
                                        frameBuffer_RGB[i] = (byte)((*(p + addrMap[i]) + *(p + addrMap[i] + 1) + *(p + addrMap[i] + 2)) / 3);
                                    }
                                    else
                                    {
                                        frameBuffer_RGB[i] = 0;
                                    }
                                    break;
                                case 5:     // 任性模式 - W为RGB最低值，RGB各减去W值
                                    byte wcolor = 0;
                                    if (addrMapOffset[i] > 3)
                                    {
                                        wcolor = min(*(p + addrMap[i]), *(p + addrMap[i] + 1), *(p + addrMap[i] + 2));
                                        frameBuffer_RGB[i] = wcolor;
                                    }
                                    else
                                    {
                                        frameBuffer_RGB[i] = (byte)(*(p + addrMap[i] + addrMapOffset[i]) - wcolor);
                                    }
                                    break;
                                case 6:     // 黄光增强 - R=G时W=R=G,否则W=0
                                    if (addrMapOffset[i] > 3)
									{
                                        if (*(p + addrMap[i] + 2) == *(p + addrMap[i] + 1))
                                        {
                                            frameBuffer_RGB[i] = (byte)(*(p + addrMap[i] + 1));
                                        }
                                        else
                                        {
                                            frameBuffer_RGB[i] = 0;
                                        }
									}
									else
									{
										frameBuffer_RGB[i] = (byte)(*(p + addrMap[i] + addrMapOffset[i]));
									}
									break;
                                case 7:     // 白光增强 - W为固定值
                                    if (addrMapOffset[i] > 3)
									{
										frameBuffer_RGB[i] = 255;
									}
									else
									{
										frameBuffer_RGB[i] = (byte)(*(p + addrMap[i] + addrMapOffset[i]));
									}
									break;
                                case 254:  // 白光可调
                                    if (addrMapOffset[i] > 3)
									{
										frameBuffer_RGB[i] = (byte)userChannelLvl;
									}
									else
									{
										frameBuffer_RGB[i] = (byte)(*(p + addrMap[i] + addrMapOffset[i]));
									}
                                    break;
                                case 0:     // 禁用
                                default:
                                    //frameBuffer_RGB[i] = *(p + addrMap[i] + addrMapOffset[i]);
                                    if (addrMapOffset[i] < 3) {
                                        frameBuffer_RGB[i] = *(p + addrMap[i] + addrMapOffset[i]);
                                    } else {
                                        frameBuffer_RGB[i] = 0;
                                    }
                                    break;
							}
						}
					}
				}
				else
				{
					typeLog("[Error] addrMap.Length != frameBuffer_RGB.Length");
				}
			}
		}

        // 辅助函数: 取三个变量的最大值
        private int max(int val1, int val2, int val3)
        {
            return Math.Max(val1, Math.Max(val2, val3));
        }

		// 辅助函数: 取三个变量的最小值
		private byte min(byte val1, byte val2, byte val3)
		{
			return Math.Min(val1, Math.Min(val2, val3));
		}

		// 辅助函数: 根据颜色获取 addrMapOffset
		private int getColorOffset(string colorString, int index)
		{
			if (colorString == null)
			{
				return -1;
			}

			if (index < 0 || index > colorString.Length - 1)
			{
				return -1;
			}

			colorString = colorString.ToUpper();
			switch (colorString[index])
			{
				case 'R':
					return 0;
				case 'G':
					return 1;
				case 'B':
					return 2;
				case 'N':
					return 3;
				case 'W':
					return 4;
				case 'Y':
					return 5;
				default:
					return -1;
			}
		}

        // 辅助函数: 根据颜色获取 addrMapOffset 将RGB顺序调换以适应bmp数据
        private int getColorOffsetEx(string colorString, int index) {
            if (colorString == null) {
                return -1;
            }

            if (index < 0 || index > colorString.Length - 1) {
                return -1;
            }

            colorString = colorString.ToUpper();
            switch (colorString[index]) {
                case 'R':
                    return 2;
                case 'G':
                    return 1;
                case 'B':
                    return 0;
                case 'N':
                    return 3;
                case 'W':
                    return 4;
                case 'Y':
                    return 5;
                default:
                    return -1;
            }
        }

		// 辅助函数: 从 lwf 文件的 xml 区获取数据
		private SystemTubeDataStore getXmlInfoFromLwf(string lwfFilePath)
		{
			Point systemArea = new Point();

			string xmlFileName = getAppPath() + @"\lwf-info.xml";

			// 判断文件是否存在
			if (File.Exists(lwfFilePath) == false)
			{
				return null;
			}

			FileStream fsLwf = new FileStream(lwfFilePath, FileMode.Open);
			FileStream fsXml = new FileStream(xmlFileName, FileMode.Create);
			if (fsLwf.Length == 0)
			{
				return null;
			}

			byte[] lwfFileBytes = new byte[fsLwf.Length];

			BinaryWriter bw = new BinaryWriter(fsXml);
			fsLwf.Read(lwfFileBytes, 0, (int)fsLwf.Length);

			int length = lwfFileBytes[0x26] << 24 | lwfFileBytes[0x27] << 16 | lwfFileBytes[0x28] << 8 | lwfFileBytes[0x29];

			byte[] xmlFileBytes = new byte[length];
			for (int i = 0; i < length; i++)
			{
				xmlFileBytes[i] = lwfFileBytes[0x100 + i];
			}
			bw.Write(xmlFileBytes);

			bw.Close();
			fsXml.Close();
			fsLwf.Close();

			/**************************************************************************/

			XmlDocument xmlDoc = new XmlDocument();
			XmlReaderSettings settings = new XmlReaderSettings();
			settings.IgnoreComments = true;
			XmlReader xmlReader = XmlReader.Create(xmlFileName, settings);
			xmlDoc.Load(xmlReader);
			xmlReader.Close();

			SystemTubeDataStore systemTubeDataStore = XmlReadWrite.ParseLwfXmlData(xmlDoc);
			systemArea.X = systemTubeDataStore.systemTube.x;
			systemArea.Y = systemTubeDataStore.systemTube.y;

			if (File.Exists(xmlFileName))
			{
				try
				{
					File.Delete(xmlFileName);
				}
				catch (Exception)
				{
				}
			}

			return systemTubeDataStore;
		}

		#endregion

		#region L9 硬件设置

		private bool pauseDownloadAddrMap = false;
		private bool forceStopDownloadAddrMap = false;
		private bool pauseClearAddrMap = false;
		private bool forceStopClearAddrMap = false;
		private bool pauseDownloadSub = false;
		private bool forceStopDownloadSub = false;

		// 【逻辑操作】向分控下发布线数据 - 线程方法
		private void subControlDownloadAddrMap(SelectedSubInfo selectedSubInfo, bool overall, bool withPreTreat, bool testMode, List<AddrMapOffset> addrMapOffsetList, int manualOffset)
		{
			try
			{
				// 当前不存在布线数据
				if (binData == null)
				{
					WriteSubStateUpdated(this, new WriteEventArgs(-1, "DownloadAddrMap", "当前不存在布线数据"));
					return;
				}

				downloadLwfWaitFlag = false;
				downloadLwfBreakFlag = false;

				// 在向分控下载配置文件期间, 停止自动发送[可选]分控配置包（通信命令包）
				bool tmpPauseSubConfigSend = false;
				if (subConfigSendEnable == true)
				{
					// 临时停止标记置位
					tmpPauseSubConfigSend = true;
					setSubConfigSendEnable(false);
					Thread.Sleep(200);
				}

				// 查询当前活跃的网卡
				int activeNetDevIndex = getActiveDevIndex();

				// 确定分控操作范围
				int offset = 0;
				int totalDataSize = 0;
				totalDataSize = selectedSubInfo.targetSubIndexList.Count * 32768;
				List<byte[]> addrMapDataList = new List<byte[]>();
				List<StartCut> startCutList = new List<StartCut>();

				// 更新操作进度 - 初始进度
				WriteEventArgs eArgsInit = new WriteEventArgs(0, "DownloadAddrMap");
				eArgsInit.current = 0;
				eArgsInit.total = selectedSubInfo.targetSubIndexList.Count;
				WriteSubStateUpdated(this, eArgsInit);

				// 是否使用布线配置表
				bool useOffsetTable = (addrMapOffsetList == null ? false : true);

				// 下发数据信息 + 设置信息
				for (int i = 0; i < selectedSubInfo.targetSubIndexList.Count; i++)
				{
					int sourceSubIndex = selectedSubInfo.sourceSubIndexList[i];
					int targetSubIndex = selectedSubInfo.targetSubIndexList[i];

					// 响应暂停
					while (pauseDownloadAddrMap)
					{
						Thread.Sleep(100);
					}
					// 响应强制停止
					if (forceStopDownloadAddrMap)
					{
						// 更新操作进度
						WriteEventArgs eArgsExit = new WriteEventArgs(offset * 100 / totalDataSize, "DownloadAddrMap");
						eArgsExit.current = i;
						eArgsExit.total = -1;
						WriteSubStateUpdated(this, eArgsExit);
						break;
					}

					byte[] addrMapData = new byte[32768];
					StartCut startCut = new StartCut();

					// 读取源数据
					if (manualOffset == 0)
					{
						Array.Copy(binData, 0x8000 + (sourceSubIndex - 1) * 32768, addrMapData, 0, 32768);
					}
					else
					{
						Array.Copy(binData, 0x8000 + (sourceSubIndex - manualOffset + 1) * 32768, addrMapData, 0, 32768);
					}

					/*********************************** 使用布线配置表调整数据 ***********************************/
					
					int offsetX = 0;
					int offsetY = 0;
					if (useOffsetTable == true)
					{
						foreach (AddrMapOffset addrMapOffset in addrMapOffsetList)
						{
							if (sourceSubIndex >= addrMapOffset.startIndex && sourceSubIndex <= addrMapOffset.endIndex)
							{
								offsetX = addrMapOffset.offsetX;
								offsetY = addrMapOffset.offsetY;
								break;
							}
						}

						string preTreatUseTableResult = "";
						preTreatUseTableResult = preTreatUseAddrMapTable(addrMapData, offsetX, offsetY);

						if (!preTreatUseTableResult.Equals(""))
						{
							string msg = "布线调整表数据错误";
							if (LanguageOper.available == true)
							{
								msg = LanguageOper.ReadValue(section, "str.1");
							}
							
							WriteSubStateUpdated(this, new WriteEventArgs(-1, "DownloadAddrMap", msg));
							
							downloadLwfWaitFlag = true;
							downloadLwfBreakFlag = false;
							
							// 等待前端界面判断后续操作
							while (true)
							{
								if (downloadLwfWaitFlag == true)
								{
									Thread.Sleep(100);
								}
								else
								{
									if (downloadLwfBreakFlag == true)
									{
										downloadLwfBreakFlag = false;

										// 更新操作进度
										WriteEventArgs eArgsReset = new WriteEventArgs(0, "DownloadAddrMap");
										eArgsReset.current = 0;
										eArgsReset.total = 0;
										WriteSubStateUpdated(this, eArgsReset);

										return;
									}
									else
									{
										break;
									}
								}
							}
						}
					}

					/********************************* End-使用布线配置表调整数据 *********************************/

					/*************************************** 预处理数据 *****************************************/

					string preTreatResult = "";
					if (withPreTreat == true)
					{
						preTreatResult = preTreatAddrMapData(addrMapData, startCut);
						startCutList.Add(startCut);
					}
					else
					{
						startCut.startX = 0;
						startCut.startY = 0;
						startCut.cutX = 1000;
						startCut.cutY = 1024;
						startCutList.Add(startCut);
					}

					addrMapDataList.Add(addrMapData);

					if (startCut.startX == -2 && startCut.startY == -2 && startCut.cutX == -2 && startCut.cutY == -2)
					{
						string msg = "下载布线：数据预处理错误";
						if (LanguageOper.available == true)
						{
							msg = LanguageOper.ReadValue(section, "str.2");
						}
						
						WriteSubStateUpdated(this, new WriteEventArgs(-1, "DownloadAddrMap", msg));
						
						return;
					}

					if (!preTreatResult.Equals(""))
					{
						string msg = string.Format("布线设计超出分控支持范围: {0}", preTreatResult);
						if (LanguageOper.available == true)
						{
							msg = string.Format(LanguageOper.ReadValue(section, "str.3"), preTreatResult);
						}

						WriteSubStateUpdated(this, new WriteEventArgs(-1, "DownloadAddrMap", msg));
						
						downloadLwfWaitFlag = true;
						downloadLwfBreakFlag = false;
						
						// 等待前端界面判断后续操作
						while (true)
						{
							if (downloadLwfWaitFlag == true)
							{
								Thread.Sleep(100);
							}
							else
							{
								if (downloadLwfBreakFlag == true)
								{
									downloadLwfBreakFlag = false;

									// 更新操作进度
									WriteEventArgs eArgsReset = new WriteEventArgs(0, "DownloadAddrMap");
									eArgsReset.current = 0;
									eArgsReset.total = 0;
									WriteSubStateUpdated(this, eArgsReset);

									return;
								}
								else
								{
									break;
								}
							}
						}
					}

					/************************************* End-预处理数据 ***************************************/

					/************************************ 将无效点指向(0,0) *************************************/

					string reset2ZeroResult = resetInvalidPoint2Zero(addrMapData);
					
					if (!reset2ZeroResult.Equals(""))
					{
						string msg = "下载布线：数据预处理错误";
						if (LanguageOper.available == true)
						{
							msg = LanguageOper.ReadValue(section, "str.2");
						}

						WriteSubStateUpdated(this, new WriteEventArgs(-1, "DownloadAddrMap", msg));

						return;
					}

					/********************************** End-将无效点指向(0,0) ***********************************/

					/*************************************** 下发异形数据 ***************************************/

					// 下发异形配置数据
					int dataSplitNum = addrMapDataList[i].Length / 256;
					byte[] splitData = new byte[256];
					for (int k = 0; k < dataSplitNum; k++)
					{
						Array.Copy(addrMapDataList[i], k * 256, splitData, 0, 256);
						writeSubMem(activeNetDevIndex, targetSubIndex, (0x8000 + (uint)k * 256), splitData, false);
					}

					// 非测试模式修改Flash
					if (testMode == false)
					{
						write2Flash(activeNetDevIndex, targetSubIndex, addrMapDataList[i], 0x5c8000, 0x4c8000, addrMapDataList[i].Length, false);
					}
					
					/************************************* End-下发异形数据 *************************************/

					/************************************* 下发截取位置数据 **************************************/

					// 准备截取位置配置数据
					byte startY_low = (byte)(0xff & startCutList[i].startY);
					byte startY_high = (byte)((0xff00 & startCutList[i].startY) >> 8);
					byte startX_low = (byte)(0xff & startCutList[i].startX);
					byte startX_high = (byte)((0xff00 & startCutList[i].startX) >> 8);
					byte cutY_low = (byte)(0xff & startCutList[i].cutY);
					byte cutY_high = (byte)((0xff00 & startCutList[i].cutY) >> 8);
					byte cutX_low = (byte)(0xff & startCutList[i].cutX);
					byte cutX_high = (byte)((0xff00 & startCutList[i].cutX) >> 8);

					byte[] infoData = new byte[256];
					infoData[0] = startY_low;
					infoData[1] = startY_high;
					infoData[2] = startX_low;
					infoData[3] = startX_high;
					infoData[4] = cutY_low;
					infoData[5] = cutY_high;
					infoData[6] = cutX_low;
					infoData[7] = cutX_high;

					// 下发截取位置配置数据
					writeSubMem(activeNetDevIndex, targetSubIndex, 0x0100, infoData, false);

					// 非测试模式，修改Flash
					if (testMode == false)
					{
						// 读回0x4c0200地址
						byte[] memData = new byte[256];
						bool timeout = readSubMem(activeNetDevIndex, targetSubIndex, 0x4c0200, ref memData);
						if (timeout == true)
						{
							MessageBox.Show("readSubMem timeout!");
						}

						// 拼接 0x4c0100 和 0x4c0200，共 512bytes
						byte[] infoData512 = new byte[512];
						Array.Copy(infoData, 0, infoData512, 0, 256);
						Array.Copy(memData, 0, infoData512, 256, 256);

						write2Flash(activeNetDevIndex, targetSubIndex, infoData512, 0x5c0100, 0x4c0100, infoData512.Length, false);
					}

					/*********************************** End-下发截取位置数据 ************************************/
				
					/************************************* 关闭自动定位使能 **************************************/

					// 处理0x1099字节（下发配置：关闭自动定位） 
					byte[] byteData = readFirmware(activeNetDevIndex, targetSubIndex, 0x4c1000, 256, false);
					byteData[0x99] = 0;	// 置0x1099字节 = 0
					writeSubMem(activeNetDevIndex, targetSubIndex, 0x1000, byteData, false);

					/*********************************** End-关闭自动定位使能 ************************************/

					// 非测试模式，修改Flash
					if (testMode == false)
					{
						write2Flash(activeNetDevIndex, targetSubIndex, byteData, 0x5c1000, 0x4c1000, byteData.Length, false);
					}

					// 更新操作进度
					offset += 32768;
					WriteEventArgs eArgs = new WriteEventArgs(offset * 100 / totalDataSize, "DownloadAddrMap");
					eArgs.current = i + 1;
					eArgs.total = selectedSubInfo.targetSubIndexList.Count;
					WriteSubStateUpdated(this, eArgs);
				}

				// 临时停止标记生效
				if (tmpPauseSubConfigSend == true)
				{
					// 恢复自动发送[可选]分控配置包
					setSubConfigSendEnable(true);
				}

				return;
			}
			catch (Exception ex)
			{
				try
				{
					WriteSubStateUpdated(this, new WriteEventArgs(-1, "DownloadAddrMap", ex.Message + ex.StackTrace));
					return;
				}
				catch (Exception)
				{
					return;
				}
			}
		}

		// 【逻辑操作】关闭分控异形映射使能 - 线程方法
		private void subControlClearAddrMap(SelectedSubInfo selectedSubInfo, bool overall, bool testMode)
		{
			try
			{
				// 在写入分控 Flash 期间, 停止自动发送[可选]分控配置包（通信命令包）
				bool tmpPauseSubConfigSend = false;
				if (subConfigSendEnable == true)
				{
					// 临时停止标记置位
					tmpPauseSubConfigSend = true;
					setSubConfigSendEnable(false);
					Thread.Sleep(200);
				}

				// 查询当前活跃的网卡
				int activeNetDevIndex = getActiveDevIndex();

				// 生成关闭异形的配置数据
				byte[] sendData = new byte[256];
				sendData[0] = 0x80;

				// 更新操作进度 - 初始进度
				WriteEventArgs eArgsInit = new WriteEventArgs(0, "ClearAddrMap");
				eArgsInit.current = 0;
				eArgsInit.total = selectedSubInfo.targetSubIndexList.Count;
				WriteSubStateUpdated(this, eArgsInit);

				int totalDataSize = 256 * selectedSubInfo.targetSubIndexList.Count;
				int finishedSize = 0;
				for (int i = 0; i < selectedSubInfo.targetSubIndexList.Count; i++)
				{
					int targetSubIndex = selectedSubInfo.targetSubIndexList[i];

					while (pauseClearAddrMap)
					{
						Thread.Sleep(100);
					}

					if (forceStopClearAddrMap)
					{
						WriteEventArgs eArgsExit = new WriteEventArgs(finishedSize * 100 / totalDataSize, "ClearAddrMap");
						eArgsExit.current = i;
						eArgsExit.total = -1;
						WriteSubStateUpdated(this, eArgsExit);
						break;
					}

					// 读取0x4c1000的配置，生成自动定位使能数据
					byte[] byteData = readFirmware(activeNetDevIndex, targetSubIndex, 0x4c1000, 256, false);
					byteData[0x99] = 1;	// 置 0x4c1099 = 1

					// 重配置0x8000字节，关闭异形，即时生效
					writeSubMem(activeNetDevIndex, targetSubIndex, 0x8000, sendData, false);
					// 重配置0x1099字节，自动定位使能，即时生效
					writeSubMem(activeNetDevIndex, targetSubIndex, 0x1000, byteData, false);
					// 非测试模式修改Flash
					if (testMode == false)
					{
						// 重配置0x4c8000字节，关闭异形
						write2Flash(activeNetDevIndex, targetSubIndex, sendData, 0x5c8000, 0x4c8000, 256, false);
						// 重配置0x4c1099字节，自动定位使能
						write2Flash(activeNetDevIndex, targetSubIndex, byteData, 0x5c1000, 0x4c1000, byteData.Length, false);
					}

					finishedSize += 256;
					WriteEventArgs eArgs = new WriteEventArgs(finishedSize * 100 / totalDataSize, "ClearAddrMap");
					eArgs.current = i;
					eArgs.total = selectedSubInfo.targetSubIndexList.Count;
					WriteSubStateUpdated(this, eArgs);
				}

				// 临时停止标记生效
				if (tmpPauseSubConfigSend == true)
				{
					// 恢复自动发送[可选]分控配置包
					setSubConfigSendEnable(true);
				}

				return;
			}
			catch (Exception ex)
			{
				WriteSubStateUpdated(this, new WriteEventArgs(-1, "ClearAddrMap"));
				typeLog("[Error] " + ex.Message + ex.StackTrace);
				return;
			}
		}

		// 【逻辑操作】向分控下发 sub 文件 - 线程方法
		private void subControlDownloadSub(byte[] subData4Send, string mode, SelectedSubInfo selectedSubInfo, bool overall, bool withSplit1)
		{
			try
			{
				// 读取 sub 文件数据 - 10 * 256 btyes
				List<byte[]> sendDataList = new List<byte[]>();
				for (int i = 0; i < 10; i++)
				{
					sendDataList.Add(new byte[256]);
				}

				// split 1
				Array.Copy(subData4Send, 0x10100, sendDataList[0], 0, 256);		// 读0x10100~0x101ff
				// split 2
				Array.Copy(subData4Send, 0x11000, sendDataList[1], 0, 256);		// 读0x11000~0x110ff
				// split 3
				Array.Copy(subData4Send, 0x13000, sendDataList[2], 0, 256);		// 读0x13000~0x130ff
				Array.Copy(subData4Send, 0x13100, sendDataList[3], 0, 256);		// 读0x13100~0x131ff
				byte[] sendData_split3 = new byte[512];
				Array.Copy(sendDataList[2], 0, sendData_split3, 0, 256);
				Array.Copy(sendDataList[3], 0, sendData_split3, 256, 256);
				// split 4
				Array.Copy(subData4Send, 0x14000, sendDataList[4], 0, 256);		// 读0x14000~0x140ff
				Array.Copy(subData4Send, 0x14100, sendDataList[5], 0, 256);		// 读0x14100~0x141ff
				byte[] sendData_split4 = new byte[512];
				Array.Copy(sendDataList[4], 0, sendData_split4, 0, 256);
				Array.Copy(sendDataList[5], 0, sendData_split4, 256, 256);
				// split 5
				Array.Copy(subData4Send, 0x17000, sendDataList[6], 0, 256);		// 读0x17000~0x170ff
				Array.Copy(subData4Send, 0x17100, sendDataList[7], 0, 256);		// 读0x17100~0x171ff
				Array.Copy(subData4Send, 0x17200, sendDataList[8], 0, 256);		// 读0x17200~0x172ff	
				byte[] sendData_split5 = new byte[768];
				Array.Copy(sendDataList[6], 0, sendData_split5, 0, 256);
				Array.Copy(sendDataList[7], 0, sendData_split5, 256, 256);
				Array.Copy(sendDataList[8], 0, sendData_split5, 512, 256);
				// split 6 - 未使用
				Array.Copy(subData4Send, 0x18000, sendDataList[9], 0, 256);		// 读0x18000~0x180ff

				// 在向分控下载配置文件期间, 停止自动发送[可选]分控配置包（通信命令包）
				bool tmpPauseSubConfigSend = false;
				if (subConfigSendEnable == true)
				{
					// 临时停止标记置位
					tmpPauseSubConfigSend = true;
					setSubConfigSendEnable(false);
					Thread.Sleep(200);
				}

				// 查询当前活跃的网卡
				int activeNetDevIndex = getActiveDevIndex();

				// 读回各分控的 0x4c0200 地址的原始数据，防止在后续写入操作中被擦除
				List<byte[]> orignDataList = new List<byte[]>();
				if (mode.Equals("apply"))
				{
					for (int i = 0; i < selectedSubInfo.targetSubIndexList.Count; i++)
					{
						int subIndex = selectedSubInfo.targetSubIndexList[i];
						byte[] memData = new byte[256];
						bool timeout = readSubMem(activeNetDevIndex, subIndex, 0x4c0200, ref memData);
						if (timeout == true)
						{
							MessageBox.Show("readSubMem timeout!");
						}
						else
						{
							orignDataList.Add(memData);
						}
					}
				}

				if (overall == true)
				{
					int totalDataSize = 2304;
					if (withSplit1 == false)
					{
						totalDataSize -= 256;
					}
					int finishedSize = 0;
                    if (WriteSubStateUpdated != null)
                    {
                        WriteSubStateUpdated(this, new WriteEventArgs(0, "DownloadSub"));
                    }

					// 写入分控 Flash 的 0x4c0000~0x4c7FFF 地址

					// split 1 - 0x4c0100（各个分控独立操作Flash）
					if (withSplit1 == true)
					{
						writeSubMem(activeNetDevIndex, 0, 0x0100, sendDataList[0], true);
						if (mode.Equals("apply"))
						{
							for (int i = 0, orignDataIndex = 0; i < selectedSubInfo.targetSubIndexList.Count; i++, orignDataIndex++)
							{
								int subIndex = selectedSubInfo.targetSubIndexList[i];

								// 拼接 0x4c0100 和 0x4c0200，共 512bytes
								byte[] infoData512 = new byte[512];
								Array.Copy(sendDataList[0], 0, infoData512, 0, 256);
								Array.Copy(orignDataList[orignDataIndex], 0, infoData512, 256, 256);

								// 写回 Flash
								write2Flash(activeNetDevIndex, subIndex, infoData512, 0x5c0100, 0x4c0100, infoData512.Length, false);
							}
						}
						finishedSize += 256;
                        if (WriteSubStateUpdated != null)
                        {
                            WriteSubStateUpdated(this, new WriteEventArgs(finishedSize * 100 / totalDataSize, "DownloadSub"));
                        }
					}

					// split 2 - 0x4c1000
					writeSubMem(activeNetDevIndex, 0, 0x1000, sendDataList[1], true);
					if (mode.Equals("apply"))
					{
						write2Flash_total(activeNetDevIndex, sendDataList[1], 0x5c1000, 0x4c1000, 256, false);
					}
					finishedSize += 256;
                    if (WriteSubStateUpdated != null)
                    {
                        WriteSubStateUpdated(this, new WriteEventArgs(finishedSize * 100 / totalDataSize, "DownloadSub"));
                    }
					// split 3 - 0x4c3000, 0x4c3100
					writeSubMem(activeNetDevIndex, 0, 0x3000, sendDataList[2], true);
					writeSubMem(activeNetDevIndex, 0, 0x3100, sendDataList[3], true);
					if (mode.Equals("apply"))
					{
						write2Flash_total(activeNetDevIndex, sendData_split3, 0x5c3000, 0x4c3000, 512, false);
					}
					finishedSize += 512;
                    if (WriteSubStateUpdated != null)
                    {
                        WriteSubStateUpdated(this, new WriteEventArgs(finishedSize * 100 / totalDataSize, "DownloadSub"));
                    }
					// split 4 - 0x4c4000, 0x4c4100
					writeSubMem(activeNetDevIndex, 0, 0x4000, sendDataList[4], true);
					writeSubMem(activeNetDevIndex, 0, 0x4100, sendDataList[5], true);
					if (mode.Equals("apply"))
					{
						write2Flash_total(activeNetDevIndex, sendData_split4, 0x5c4000, 0x4c4000, 512, false);
					}
					finishedSize += 512;
                    if (WriteSubStateUpdated != null)
                    {
                        WriteSubStateUpdated(this, new WriteEventArgs(finishedSize * 100 / totalDataSize, "DownloadSub"));
                    }
					// split 5 - 0x4c7000, 0x4c7100, 0x4c7200
					writeSubMem(activeNetDevIndex, 0, 0x7000, sendDataList[6], true);
					writeSubMem(activeNetDevIndex, 0, 0x7100, sendDataList[7], true);
					writeSubMem(activeNetDevIndex, 0, 0x7200, sendDataList[8], true);
					if (mode.Equals("apply"))
					{
						write2Flash_total(activeNetDevIndex, sendData_split5, 0x5c7000, 0x4c7000, 768, false);
					}
					finishedSize += 768;
                    if (WriteSubStateUpdated != null)
                    {
                        WriteSubStateUpdated(this, new WriteEventArgs(totalDataSize * 100 / totalDataSize, "DownloadSub"));
                    }
				}
				else
                {
                    int totalDataSize = 2304 * selectedSubInfo.targetSubIndexList.Count;
                    if (withSplit1 == false)
                    {
                        totalDataSize -= 256 * selectedSubInfo.targetSubIndexList.Count;
                    }
                    int finishedSize = 0;
                    if (WriteSubStateUpdated != null)
                    {
                        WriteSubStateUpdated(this, new WriteEventArgs(0, "DownloadSub"));
                    }
                    for (int i = 0, orignDataIndex = 0; i < selectedSubInfo.targetSubIndexList.Count; i++, orignDataIndex++)
                    {
                        int subIndex = selectedSubInfo.targetSubIndexList[i];
                        while (pauseDownloadSub)
                        {
                            Thread.Sleep(100);
                        }

                        if (forceStopDownloadSub)
                        {
                            WriteEventArgs eArgsExit = new WriteEventArgs(finishedSize * 100 / totalDataSize, "DownloadSub");
                            eArgsExit.current = i;
                            eArgsExit.total = -1;
                            if (WriteSubStateUpdated != null)
                            {
                                WriteSubStateUpdated(this, eArgsExit);
                            }
                            break;
                        }

                        // 写入分控 Flash 的 0x4c0000~0x4c7FFF 地址

                        // split 1 - 0x4c0100
                        if (withSplit1 == true)
                        {
                            writeSubMem(activeNetDevIndex, subIndex, 0x0100, sendDataList[0], false);
                            if (mode.Equals("apply"))
                            {
                                // 拼接 0x4c0100 和 0x4c0200，共 512bytes
                                byte[] infoData512 = new byte[512];
                                Array.Copy(sendDataList[0], 0, infoData512, 0, 256);
                                Array.Copy(orignDataList[orignDataIndex], 0, infoData512, 256, 256);

                                // 写回Flash
                                write2Flash(activeNetDevIndex, subIndex, infoData512, 0x5c0100, 0x4c0100, infoData512.Length, false);
                            }
                            finishedSize += 256;
                            if (WriteSubStateUpdated != null)
                            {
                                WriteSubStateUpdated(this, new WriteEventArgs(finishedSize * 100 / totalDataSize, "DownloadSub"));
                            }
                        }

                        // split 2 - 0x4c1000
                        writeSubMem(activeNetDevIndex, subIndex, 0x1000, sendDataList[1], false);
                        if (mode.Equals("apply"))
                        {
                            write2Flash(activeNetDevIndex, subIndex, sendDataList[1], 0x5c1000, 0x4c1000, 256, false);
                        }
                        finishedSize += 256;
                        if (WriteSubStateUpdated != null)
                        {
                            WriteSubStateUpdated(this, new WriteEventArgs(finishedSize * 100 / totalDataSize, "DownloadSub"));
                        }

                        // split 3 - 0x4c3000, 0x4c3100
                        writeSubMem(activeNetDevIndex, subIndex, 0x3000, sendDataList[2], false);
                        writeSubMem(activeNetDevIndex, subIndex, 0x3100, sendDataList[3], false);
                        if (mode.Equals("apply"))
                        {
                            write2Flash(activeNetDevIndex, subIndex, sendData_split3, 0x5c3000, 0x4c3000, 512, false);
                        }
                        finishedSize += 512;
                        if (WriteSubStateUpdated != null)
                        {
                            WriteSubStateUpdated(this, new WriteEventArgs(finishedSize * 100 / totalDataSize, "DownloadSub"));
                        }
                        // split 4 - 0x4c4000, 0x4c4100
                        writeSubMem(activeNetDevIndex, subIndex, 0x4000, sendDataList[4], false);
                        writeSubMem(activeNetDevIndex, subIndex, 0x4100, sendDataList[5], false);
                        if (mode.Equals("apply"))
                        {
                            write2Flash(activeNetDevIndex, subIndex, sendData_split4, 0x5c4000, 0x4c4000, 512, false);
                        }
                        finishedSize += 512;
                        if (WriteSubStateUpdated != null)
                        {
                            WriteSubStateUpdated(this, new WriteEventArgs(finishedSize * 100 / totalDataSize, "DownloadSub"));
                        }
                        // split 5 - 0x4c7000, 0x4c7100, 0x4c7200
                        writeSubMem(activeNetDevIndex, subIndex, 0x7000, sendDataList[6], false);
                        writeSubMem(activeNetDevIndex, subIndex, 0x7100, sendDataList[7], false);
                        writeSubMem(activeNetDevIndex, subIndex, 0x7200, sendDataList[8], false);
                        if (mode.Equals("apply"))
                        {
                            write2Flash(activeNetDevIndex, subIndex, sendData_split5, 0x5c7000, 0x4c7000, 768, false);
                        }
                        finishedSize += 768;
                        if (WriteSubStateUpdated != null)
                        {
                            WriteSubStateUpdated(this, new WriteEventArgs(finishedSize * 100 / totalDataSize, "DownloadSub"));
                        }
                    }
                }

				// 临时停止标记生效
				if (tmpPauseSubConfigSend == true)
				{
					// 恢复自动发送[可选]分控配置包
					setSubConfigSendEnable(true);
				}

				return;
			}
			catch (Exception ex)
			{
				typeLog("[Error] " + ex.Message + ex.StackTrace);
                if (WriteSubStateUpdated != null)
                {
                    WriteSubStateUpdated(this, new WriteEventArgs(-1, "DownloadSub"));
                }
				return;
			}
		}

		// 内部函数 - 下发异形配置数据前使用布线配置表预处理
		private string preTreatUseAddrMapTable(byte[] addrMapData, int offsetX, int offsetY)
		{
			try
			{
				int lampNum = addrMapData.Length / 4;	// 灯具数量

				for (int i = 0; i < lampNum; i++)
				{
					// 灯具映射信息, 先垂直后水平, 先高位后低位
					int y = addrMapData[i * 4 + 0] << 8 | addrMapData[i * 4 + 1];
					int x = addrMapData[i * 4 + 2] << 8 | addrMapData[i * 4 + 3];

					if (x != 0xffff)
					{
						x -= offsetX;
						y -= offsetY;
					}

					if (x < 0)
					{
						x = 0;
					}

					if (y < 0)
					{
						y = 0;
					}

					// 数据写回, 先垂直（y）后水平（x）, 先高位后低位
					addrMapData[i * 4 + 0] = (byte)((0xff00 & y) >> 8);		// 垂直（y）高位
					addrMapData[i * 4 + 1] = (byte)(0xff & y);				// 垂直（y）低位
					addrMapData[i * 4 + 2] = (byte)((0xff00 & x) >> 8);		// 水平（x）高位
					addrMapData[i * 4 + 3] = (byte)(0xff & x);				// 水平（x）低位
				}

				return "";
			}
			catch (Exception)
			{
				return "Exception";
			}
		}

		// 内部函数 - 下发异型配置数据前将无效点指向(0,0)
		private string resetInvalidPoint2Zero(byte[] addrMapData)
		{
			try
			{
				int lampNum = addrMapData.Length / 4;	// 灯具数量
				for (int i = 0; i < lampNum; i++)
				{
					// 灯具映射信息, 先垂直后水平, 先高位后低位
					int y = addrMapData[i * 4 + 0] << 8 | addrMapData[i * 4 + 1];
					int x = addrMapData[i * 4 + 2] << 8 | addrMapData[i * 4 + 3];

					if(x == 0xffff && y == 0xffff)
					{
						x = 0;
						y = 0;
					}

					// 数据写回, 先垂直（y）后水平（x）, 先高位后低位
					addrMapData[i * 4 + 0] = (byte)((0xff00 & y) >> 8);		// 垂直（y）高位
					addrMapData[i * 4 + 1] = (byte)(0xff & y);				// 垂直（y）低位
					addrMapData[i * 4 + 2] = (byte)((0xff00 & x) >> 8);		// 水平（x）高位
					addrMapData[i * 4 + 3] = (byte)(0xff & x);				// 水平（x）低位
				}
				
				return "";
			}
			catch (Exception)
			{
				return "Exception";
			}
		}

		// 内部函数 - 下发异形配置数据前的预处理：根据起点修改偏移量
		private string preTreatAddrMapData(byte[] addrMapData, StartCut startCut)
		{
			try
			{
				int minX = int.MaxValue;
				int minY = int.MaxValue;
				int maxX = int.MinValue;
				int maxY = int.MinValue;
				int diffX = 0;
				int diffY = 0;

				int lampNum = addrMapData.Length / 4;	// 灯具数量

				// 统计 MinX、MinY、MaxX、MaxY、DiffX、DiffY
				for (int i = 0; i < lampNum; i++)
				{
					// 灯具映射信息, 先垂直后水平, 先高位后低位
					int y = addrMapData[i * 4 + 0] << 8 | addrMapData[i * 4 + 1];
					int x = addrMapData[i * 4 + 2] << 8 | addrMapData[i * 4 + 3];

					// 无效点不参与 maxX、maxY 计算
					if (x != 0xffff)	// 坐标 0、坐标 0xffff 不包含在操作范围内
					{
						if (y == 0xffff)
						{
							;
						}

						if (x > maxX)
						{
							maxX = x;
						}
						if (x < minX)
						{
							minX = x;
						}
						if (y > maxY)
						{
							maxY = y;
						}
						if (y < minY)
						{
							minY = y;
						}
					}
				}
				diffX = maxX - minX + 1;
				diffY = maxY - minY + 1;

				// 数据变换
				int startX, startY, cutX, cutY;
				if (diffX <= 1000 && diffY <= 1024)
				{
					startX = minX;
					startY = minY;
					cutX = diffX + 8;
					cutY = 1024;

					for (int i = 0; i < lampNum; i++)
					{
						// 灯具映射信息, 先垂直后水平, 先高位后低位
						int y = addrMapData[i * 4 + 0] << 8 | addrMapData[i * 4 + 1];
						int x = addrMapData[i * 4 + 2] << 8 | addrMapData[i * 4 + 3];

						if (x != 0xffff)	//坐标 0xffff 不包含在操作范围内
						{
							// 其余所有点 x 减去 startX
							x -= startX;
							if (x < 0)
							{
								x = 0;
							}

							// 其余所有点 y 减去 startY
							y -= startY;
							if (y < 0)
							{
								y = 0;
							}
						}

						// 数据写回, 先垂直（y）后水平（x）, 先高位后低位
						addrMapData[i * 4 + 0] = (byte)((0xff00 & y) >> 8);		// 垂直（y）高位
						addrMapData[i * 4 + 1] = (byte)(0xff & y);				// 垂直（y）低位
						addrMapData[i * 4 + 2] = (byte)((0xff00 & x) >> 8);		// 水平（x）高位
						addrMapData[i * 4 + 3] = (byte)(0xff & x);				// 水平（x）低位
					}

					startCut.startX = startX;
					startCut.startY = startY;
					startCut.cutX = cutX;
					startCut.cutY = cutY;

					return "";
				}
				else if (diffX <= 2000 && diffY <= 512)
				{
					startX = minX;
					startY = minY;
					cutX = diffX + 8;
					cutY = 512;

					for (int i = 0; i < lampNum; i++)
					{
						// 灯具映射信息, 先垂直（y）后水平（x）, 先高位后低位
						int y = addrMapData[i * 4 + 0] << 8 | addrMapData[i * 4 + 1];
						int x = addrMapData[i * 4 + 2] << 8 | addrMapData[i * 4 + 3];

						if (x != 0xffff)	//坐标 0xffff 不包含在操作范围内
						{
							x -= startX;
							if (x < 0)
							{
								x = 0;
							}
							y -= startY;
							if (y < 0)
							{
								y = 0;
							}
						}

						// 数据写回, 先垂直（y）后水平（x）, 先高位后低位
						addrMapData[i * 4 + 0] = (byte)((0xff00 & y) >> 8);		// 垂直（y）高位
						addrMapData[i * 4 + 1] = (byte)(0xff & y);				// 垂直（y）低位
						addrMapData[i * 4 + 2] = (byte)((0xff00 & x) >> 8);		// 水平（x）高位
						addrMapData[i * 4 + 3] = (byte)(0xff & x);				// 水平（x）低位
					}

					startCut.startX = startX;
					startCut.startY = startY;
					startCut.cutX = cutX;
					startCut.cutY = cutY;

					return "";
				}
				else
				{
					// 布线设计超出分控支持范围
					startX = 0;
					startY = 0;
					cutX = 1000;
					cutY = 1024;

					startCut.startX = startX;
					startCut.startY = startY;
					startCut.cutX = cutX;
					startCut.cutY = cutY;

					return "minX = " + minX + ", maxX = " + maxX + ", minY = " + minY + ", maxY = " + maxY;
				}
			}
			catch (Exception)
			{
				// 变换过程存在异常
				startCut.startX = -2;
				startCut.startY = -2;
				startCut.cutX = -2;
				startCut.cutY = -2;

				return "Exception";
			}
		}

		#endregion

		#region 子控（解码器）

		/************************************** 子控级基础读写 ********************************/

		// 发送 DMX 命令
		private void sendDmxCmd(int devIndex, int subIndex, int subPortIndex, byte[] cmdData)
		{
			uint memAddr = (uint)(0x20A00000 + (subPortIndex << 8));
			if (subIndex == 0)
			{
				writeSubMem(devIndex, subIndex, memAddr, cmdData, true);
			}
			else
			{
				writeSubMem(devIndex, subIndex, memAddr, cmdData, false);
			}
		}

		// 读取 DMX 结果
		private bool readDmxAck(int devIndex, int subIndex, int subPortIndex, byte[] ackData)
		{
			uint memAddr = (uint)0x20A10000;
			bool timeout = readSubMem(devIndex, subIndex, memAddr, ref ackData);
			return timeout;
		}

		// 发送 DMX 命令 - 带有基地址参数
		private void sendDmxCmdWithBaseAddr(int devIndex, int subIndex, int subPortIndex, byte[] cmdData, uint baseAddr)
		{
			uint memAddr = (uint)(baseAddr + (subPortIndex << 8));
			if (subIndex == 0)
			{
				writeSubMem(devIndex, subIndex, memAddr, cmdData, true);
			}
			else
			{
				writeSubMem(devIndex, subIndex, memAddr, cmdData, false);
			}
		}

		// 读取 DMX 结果 - 带有基地址参数
		private bool readDmxAckWithBaseAddr(int devIndex, int subIndex, int subPortIndex, byte[] ackData, uint baseAddr)
		{
			uint memAddr = baseAddr + (uint)subPortIndex;
			bool timeout = readSubMem(devIndex, subIndex, memAddr, ref ackData);
			return timeout;
		}

		/************************************************************************************/

		// 向子控发送命令 - 返回发包序号
		public int sendMiniCmd(int devIndex, int subIndex, int subPortIndex, int miniIndex, byte[] cmdData, int cmdLength)
		{
			// 计算当前发包序号
			miniCommNum++;
			if (miniCommNum >= 255)
			{
				miniCommNum = 1;
			}

			// 包头标识
			cmdData[0] = 0x5A;
			cmdData[1] = 0xA5;

			// 发包序号
			cmdData[3] = (byte)(miniCommNum & 0xff);

			// 目的地址号
			if (miniIndex != -1)
			{
				cmdData[4] = (byte)(miniIndex & 0xff);
				cmdData[5] = (byte)((miniIndex >> 8) & 0xff);
			}
			else
			{
				cmdData[4] = (byte)(0xff);
				cmdData[5] = (byte)(0xff);
			}

			// 包长度
			cmdData[6] = (byte)((cmdLength - 1) & 0xff);

			// 校验
			cmdData[cmdLength - 4] = 0x00;
			cmdData[cmdLength - 3] = 0x00;
			cmdData[cmdLength - 2] = 0x00;
			cmdData[cmdLength - 1] = 0x00;

			for (int i = cmdLength; i < 256; i++)
			{
				cmdData[i] = 0;
			}

			sendDmxCmd(devIndex, subIndex, subPortIndex, cmdData);

			return miniCommNum;
		}

        // 向子控发送命令 - 返回发包序号 - 带校验和
        public int sendMiniCmdWithCheck(int devIndex, int subIndex, int subPortIndex, int miniIndex, byte[] cmdData, int cmdLength)
        {
            // 计算当前发包序号
            miniCommNum++;
            if (miniCommNum >= 255)
            {
                miniCommNum = 1;
            }

            // 包头标识
            cmdData[0] = 0x5A;
            cmdData[1] = 0xA5;

            // 发包序号
            cmdData[3] = (byte)(miniCommNum & 0xff);

            // 目的地址号
            if (miniIndex != -1)
            {
                cmdData[4] = (byte)(miniIndex & 0xff);
                cmdData[5] = (byte)((miniIndex >> 8) & 0xff);
            }
            else
            {
                cmdData[4] = (byte)(0xff);
                cmdData[5] = (byte)(0xff);
            }

            // 包长度
            cmdData[6] = (byte)((cmdLength - 1) & 0xff);

            // 校验和
            byte checksum = 0;
            for (int i = 0; i < cmdLength; i++)
            {
                checksum = (byte)((checksum + cmdData[i]) & 0xff);
            }
            cmdData[cmdLength - 1] = checksum;

            for (int i = cmdLength; i < 256; i++)
            {
                cmdData[i] = 0;
            }

            sendDmxCmd(devIndex, subIndex, subPortIndex, cmdData);

            return miniCommNum;
        }

        // 向子控发送命令 - 返回发包序号 - 带校验和 - cmdData[6]长度包括校验位
        public int sendMiniCmdWithCheckEx(int devIndex, int subIndex, int subPortIndex, int miniIndex, byte[] cmdData, int cmdLength) {
            // 计算当前发包序号
            miniCommNum++;
            if (miniCommNum >= 255) {
                miniCommNum = 1;
            }

            // 包头标识
            cmdData[0] = 0x5A;
            cmdData[1] = 0xA5;

            // 发包序号
            cmdData[3] = (byte)(miniCommNum & 0xff);

            // 目的地址号
            if (miniIndex != -1) {
                cmdData[4] = (byte)(miniIndex & 0xff);
                cmdData[5] = (byte)((miniIndex >> 8) & 0xff);
            } else {
                cmdData[4] = (byte)(0xff);
                cmdData[5] = (byte)(0xff);
            }

            // 包长度
            cmdData[6] = (byte)(cmdLength & 0xff);

            // 校验和
            byte checksum = 0;
            for (int i = 0; i < cmdLength; i++) {
                checksum = (byte)((checksum + cmdData[i]) & 0xff);
            }
            cmdData[cmdLength - 1] = checksum;

            for (int i = cmdLength; i < 256; i++) {
                cmdData[i] = 0;
            }

            sendDmxCmd(devIndex, subIndex, subPortIndex, cmdData);

            return miniCommNum;
        }

        // 向子控发送命令 - 返回发包序号
        public int sendAmpCmd(int devIndex, int subIndex, int subPortIndex, byte[] cmdData, int cmdLength,int addr = 0xffff)
        {
            // 计算当前发包序号
            miniCommNum++;
            if (miniCommNum >= 255)
            {
                miniCommNum = 1;
            }

            // 包头标识
            cmdData[0] = 0x5A;
            cmdData[1] = 0xC3;

            // 指令验证
            cmdData[3] = (byte)((~cmdData[2]) & 0xff);

            // 地址填充
            cmdData[4] = (byte)((addr >> 8) & 0xff);
            cmdData[5] = (byte)(addr & 0xff);
            // 包长度
            cmdData[6] = (byte)((cmdLength - 1) & 0xff);

            // 发包序号
            cmdData[8] = (byte)(miniCommNum & 0xff);

            // 校验
            cmdData[cmdLength - 4] = 0x00;
            cmdData[cmdLength - 3] = 0x00;
            cmdData[cmdLength - 2] = 0x00;
            cmdData[cmdLength - 1] = 0x00;

            for (int i = cmdLength; i < 256; i++)
            {
                cmdData[i] = 0;
            }

            sendDmxCmd(devIndex, subIndex, subPortIndex, cmdData);

            return miniCommNum;
        }

		// 读取子控反馈包 - 返回读操作成功标记
		public bool getMiniAck(int devIndex, int subIndex, int subPortIndex, byte[] ackData, int cmdCommNum)
		{
			Thread.Sleep(30);

			bool timeout = readDmxAck(devIndex, subIndex, subPortIndex, ackData);

			if (!timeout)
			{
				if (ackData[3] != cmdCommNum)	// 包序号不一致
				{
					typeLog("[Error] Wrong cmdCommNum: ackData[3] = " + ackData[3] + ", cmdCommNum = " + cmdCommNum);
					return false;
				}
				else
				{
					return true;
				}
			}
			else
			{
				return false;
			}
		}

        public bool getAmpAck(int devIndex, int subIndex, int subPortIndex, byte[] ackData, int cmdCommNum)
        {
            Thread.Sleep(30);

            bool timeout = readDmxAck(devIndex, subIndex, subPortIndex, ackData);

            if (!timeout)
            {
                if (ackData[8] != cmdCommNum)	// 包序号不一致
                {
                    typeLog("[Error] Wrong cmdCommNum: ackData[8] = " + ackData[8] + ", cmdCommNum = " + cmdCommNum);
                    return false;
                }
                else
                {
                    return true;
                }
            }
            else
            {
                return false;
            }
        }

		// 向子控发送命令 - 返回发包序号（带有基地址参数）
		private int sendMiniCmdWithBaseAddr(int devIndex, int subIndex, int subPortIndex, int miniIndex, byte[] cmdData, int cmdLength, uint baseAddr)
		{
			// 计算当前发包序号
			miniCommNum++;
			if (miniCommNum >= 255)
			{
				miniCommNum = 1;
			}

			// 包头标识
			cmdData[0] = 0x5A;
			cmdData[1] = 0xA5;

			// 发包序号
			cmdData[3] = (byte)(miniCommNum & 0xff);

			// 目的地址号
			if (miniIndex != -1)
			{
				cmdData[4] = (byte)(miniIndex & 0xff);
				cmdData[5] = (byte)((miniIndex >> 8) & 0xff);
			}
			else
			{
				cmdData[4] = (byte)(0xff);
				cmdData[5] = (byte)(0xff);
			}

			// 包长度
			cmdData[6] = (byte)((cmdLength - 1) & 0xff);

			// 校验
			cmdData[cmdLength - 4] = 0x00;
			cmdData[cmdLength - 3] = 0x00;
			cmdData[cmdLength - 2] = 0x00;
			cmdData[cmdLength - 1] = 0x00;

			for (int i = cmdLength; i < 256; i++)
			{
				cmdData[i] = 0;
			}

			if (subIndex == 1 && subPortIndex == 1 && miniIndex == 1)
			{
				cmdData[2] = cmdData[2];
			}

			sendDmxCmdWithBaseAddr(devIndex, subIndex, subPortIndex, cmdData, baseAddr);

			return miniCommNum;
		}

        // 向子控发送命令 - 返回发包序号（带有基地址参数）
        private int sendMiniCmdWithBaseAddrAndCheck(int devIndex, int subIndex, int subPortIndex, int miniIndex, byte[] cmdData, int cmdLength, uint baseAddr)
        {
            // 计算当前发包序号
            miniCommNum++;
            if (miniCommNum >= 255)
            {
                miniCommNum = 1;
            }

            // 包头标识
            cmdData[0] = 0x5A;
            cmdData[1] = 0xA5;

            // 发包序号
            cmdData[3] = (byte)(miniCommNum & 0xff);

            // 目的地址号
            if (miniIndex != -1)
            {
                cmdData[4] = (byte)(miniIndex & 0xff);
                cmdData[5] = (byte)((miniIndex >> 8) & 0xff);
            }
            else
            {
                cmdData[4] = (byte)(0xff);
                cmdData[5] = (byte)(0xff);
            }

            // 包长度
            cmdData[6] = (byte)((cmdLength - 1) & 0xff);

            // 校验和
            byte checksum = 0;
            for (int i = 0; i < cmdLength; i++)
            {
                checksum = (byte)((checksum + cmdData[i]) & 0xff);
            }
            cmdData[cmdLength - 1] = checksum;

            for (int i = cmdLength; i < 256; i++)
            {
                cmdData[i] = 0;
            }

            if (subIndex == 1 && subPortIndex == 1 && miniIndex == 1)
            {
                cmdData[2] = cmdData[2];
            }

            sendDmxCmdWithBaseAddr(devIndex, subIndex, subPortIndex, cmdData, baseAddr);

            return miniCommNum;
        }

		// 读取子控反馈包 - 返回读操作成功标记（带有基地址参数）
		private bool getMiniAckWithBaseAddr(int devIndex, int subIndex, int subPortIndex, byte[] ackData, uint baseAddr, int cmdCommNum)
		{
			Thread.Sleep(30);

			bool timeout = readDmxAckWithBaseAddr(devIndex, subIndex, subPortIndex, ackData, baseAddr);

			if (!timeout)
			{
				if (ackData[3] != cmdCommNum)// 包序号不一致
				{
					typeLog("[Error] Wrong cmdCommNum: ackData[3] = " + ackData[3] + ", cmdCommNum = " + cmdCommNum);
					return false;
				}
				else
				{
					return true;
				}
			}
			else
			{
				return false;
			}
		}

		/************************************************************************************/


		#endregion

		#region LedDebug

		// 根据 LedDebug 设置的有效分控、有效端口进行数据筛选
		private void filtFrameData(BitmapData bmpData)
		{
			unsafe
			{
				// 根据 LedDebug 设置的有效分控、有效端口进行数据筛选
				if (effectLedDebugSub == 0 && effectLedDebugPort == 0)
				{
					// 全都分控、全部端口均有效，数据无变化
				}
				else if (effectLedDebugSub != 0 && effectLedDebugPort == 0)
				{
					// 特定分控、所有端口有效，保留 8 行数据
					int effectStartJ = (effectLedDebugSub - 1) * 8;
					int effectEndJ = (effectLedDebugSub) * 8;
					for (int j = 0; j < captureSize.Y; j++)
					{
						byte* pp = (byte*)bmpData.Scan0 + j * bmpData.Stride;
						if (j >= effectStartJ && j < effectEndJ)
						{
							// 保留数据
						}
						else
						{
							// 清除数据
							for (int i = 0; i < captureSize.X; i++)
							{
								int offset = (j * captureSize.X + i) * 3;
								*(pp + 2) = 0;
								*(pp + 1) = 0;
								*(pp + 0) = 0;
								pp += 3;
							}
						}
					}
				}
				else if (effectLedDebugSub != 0 && effectLedDebugPort != 0)
				{
					// 特定分控、特定端口有效，保留 1 行数据
					int effectJ = (effectLedDebugSub - 1) * 8 + (effectLedDebugPort - 1);
					for (int j = 0; j < captureSize.Y; j++)
					{
						byte* pp = (byte*)bmpData.Scan0 + j * bmpData.Stride;
						if (j == effectJ)
						{
							// 保留数据
						}
						else
						{
							// 清除数据
							for (int i = 0; i < captureSize.X; i++)
							{
								int offset = (j * captureSize.X + i) * 3;
								*(pp + 2) = 0;
								*(pp + 1) = 0;
								*(pp + 0) = 0;
								pp += 3;
							}
						}
					}
				}
			}
		}

		#endregion

		#region 日志操作

		// 打印日志
		public void typeLog(Object obj, LogFile logFile = LogFile.Normal, LogTarget logTarget = LogTarget.Both)
		{
			if (typeLogEnable == false)
			{
				return;
			}

			string str = obj.ToString();

			if (logTarget == LogTarget.CmdOnly)
			{
				Console.WriteLine(str);
			}
			else
			{
				if (logTarget == LogTarget.Both)
				{
					Console.WriteLine(str);
				}

				if (logFile == LogFile.Normal && normalLogEnable == true)
				{
					Log.NormalLogger.Info(str);
				}
				else if (logFile == LogFile.Test && testLogEnable == true)
				{
					Log.TestLogger.Info(str);
				}
				else if (logFile == LogFile.DetectNet && detectNetLogEnable == true)
				{
					Log.DetectNetLogger.Info(str);
				}
				else if (logFile == LogFile.Debug && debugLogEnable == true)
				{
					Log.DebugLogger.Info(str);
				}
			}
		}

		#endregion

        #region 状态更新

        // 更新状态
        private void onUpdateStatusStr(string status)
        {
            if (UpdateStatusStr != null)
            {
                UpdateStatusStr(status);
            }
        }

        #endregion

        #region ArtNet

        public const UInt16 ArtNetPort = 0x1936;

        private const int ART_SEND_RETRY = 1;
        private const int UNV_QUERY_TIMEOUT = 2000;
        private const int UID_QUERY_TIMEOUT = 50;
        private const int UID_REQUERY_TIMEOUT = 10;
        private const int RDM_TIMEOUT = 50;
        private int ChannelCount = 8;
        private byte NetInfo = 0x00;
        private byte SubNetInfo = 0x00;
        private byte[] AddrInfo = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
        private bool sendPending = false;
        private bool isTimeoutArt = false;
        private bool isSendingArt = false;
        private bool isCancelArt = false;
        private bool sendErrorArt = false;
        private ArtPacket readArtPacket = null;
        private ArtPacketType currArtPacketType = ArtPacketType.Invalid;
        private bool requeryOnce = false;       // 用于保证只有第一次超过阈值时才发起检测
        private int requeryInterval = 1000;     // 重检测时间间隔ms
        private int requeryTime = 0;            // 重检测次数
        private bool requeryInfoTold = false;   // 当前检测信息是否已被告知
        private object artDevListLock = new object();

        public Dictionary<byte, List<ArtPortInfo>> ArtDevList;
        public ArtPortInfo[] ArtDevList_uu;
        public ArtDeviceChain ArtDevDetailList;
        public ArtDeviceChain BaseArtDevDetailList;
        public float ArtSubQueryThreshold = 0.5f;   // 50%
        public int ArtSubQueryDelaySec = 300;       // 5min
        public int ArtRequeryWaitTime = 120;        // 2min

        public event Action<ArtPollReply, string, string> OnPollReplyReceived;
        public event Action<ArtTodData, string, string> OnTodDataReceived;
        public event Action<ArtRDM, string, string> OnRdmReceived;
        // RDMCore
        private RDMCore rdmCore;
        private int artPacketNum = 0;

        private void sendArtPacket(NetDevice dev, string ip, string mac, byte[] packet, Callback cb, int retry, int timeout = 1000) {
            isSendingArt = true;
            isCancelArt = false;
            var thread = new Thread(() => { sendArtThread(dev, ip, mac, packet, cb, retry, timeout); });
            thread.Start();
        }

        private void sendArtThread(NetDevice dev, string ip, string mac, byte[] packet, Callback callback, int retry, int timeout = 1000) {
            if (packet == null || packet.Length == 0) {
                callback(false, "Invalid Art Packet");
                return;
            }
            var len = packet.Length;
            while (retry > 0) {
                retry--;
                //Console.WriteLine(string.Format("{0},start",DateTime.Now.ToString()));
                var startTime = DateTime.Now;
                isTimeoutArt = false;
                sendPending = true;
                dev.packetSender.sendArtNetPacket(ip, mac, len, packet);
                while (!isCancelArt && sendPending) {
                    Thread.Sleep(1);
                    var duration = DateTime.Now - startTime;
                    if (duration.TotalMilliseconds >= timeout) {
                        //Console.WriteLine(string.Format("{0},timeout", DateTime.Now.ToString()));
                        isTimeoutArt = true;
                        break;
                    }
                }
                //Console.WriteLine(string.Format("{0},end", DateTime.Now.ToString()));
                // NOTE: 防止超时以后再收到结果
                sendPending = false;

                if (isCancelArt) {
                    break;
                }

                if (!isTimeoutArt) {
                    // 未超时
                    break;
                }

                //Console.WriteLine(">>>> send retry {0}", retry);
                Thread.Sleep(100);
            }

            currArtPacketType = ArtPacketType.Invalid;

            isSendingArt = false;

            if (isCancelArt) {
                callback(false, "操作取消");
            } else if (isTimeoutArt) {
                callback(false, "通信超时");
            } else if (sendErrorArt) {
                callback(false, "操作失败");
            } else {
                callback(true, "");
            }
        }

        private void sendArtPollPacket(NetDevice dev, string ip, Callback cb) {
            lock (artDevListLock) {
                ArtPoll pollPacket = new ArtPoll();
                pollPacket.autoReply = false;
                pollPacket.diagnostics = false;
                pollPacket.diagnosticsUincast = false;
                pollPacket.VLC = false;
                byte[] packet = pollPacket.generatePacket();

                currArtPacketType = ArtPacketType.OpPoll;

                sendArtPacket(dev, ip, dev.BroadcastMAC, packet, cb, ART_SEND_RETRY, UNV_QUERY_TIMEOUT);
            }
        }

        private void sendArtToDRequestPacket(NetDevice dev, int unv, Callback cb) {
            lock (artDevListLock) {
                byte net = (byte)((unv >> 8) & 0xff);
                byte addr = (byte)(unv & 0xff);
                ArtTodRequest todreqPacket = new ArtTodRequest();
                todreqPacket.Net = net;
                todreqPacket.AddrCount = 1;
                todreqPacket.Addr[0] = addr;
                todreqPacket.Command = 0;
                byte[] packet = todreqPacket.generatePacket();


                currArtPacketType = ArtPacketType.OpTodRequest;

                sendArtPacket(dev, dev.BroadcastIP, dev.BroadcastMAC, packet, cb, ART_SEND_RETRY, UID_QUERY_TIMEOUT);
            }
        }

        private void sendArtToDControlPacket(NetDevice dev, int unv, Callback cb) {
            lock (artDevListLock) {
                var net = (byte)((unv >> 8) & 0xff);
                var addr = (byte)(unv & 0xff);
                ArtTodControl todctrlPacket = new ArtTodControl();
                todctrlPacket.Net = net;
                todctrlPacket.Command = 0x01;
                todctrlPacket.Address = addr;
                byte[] packet = todctrlPacket.generatePacket();

                currArtPacketType = ArtPacketType.OpTodControl;

                sendArtPacket(dev, dev.BroadcastIP, dev.BroadcastMAC, packet, cb, ART_SEND_RETRY, UID_REQUERY_TIMEOUT);
            }
        }

        private void sendArtRDMPacket(NetDevice dev, byte[] rdm, int unv, Callback cb) {
            var net = (byte)((unv >> 8) & 0xff);
            var addr = (byte)(unv & 0xff);

            ArtRDM rdmPacket = new ArtRDM(rdm);
            rdmPacket.Address = addr;
            rdmPacket.Net = net;
            rdmPacket.Command = 0;

            byte[] packet = rdmPacket.generatePacket();

            currArtPacketType = ArtPacketType.OpRDM;

            sendArtPacket(dev, dev.BroadcastIP, dev.BroadcastMAC, packet, cb, ART_SEND_RETRY, RDM_TIMEOUT);
        }

        private void handleArtNetPacket(ArtPacket packet, string ip, string mac) {
            if (!isSendingArt) {
                return;
            }
            //Console.WriteLine(string.Format("handle - {0} code:{1} type:{2}", artPacketNum++, (ArtPacketType)packet.OpCode, currArtPacketType));
            try {
                var isValid = false;
                switch (packet.OpCode) { //判断valid
                    case ArtPacket.OpPollReply:
                        if (currArtPacketType != ArtPacketType.OpPoll) {
                            break;
                        }
                        isValid = true;
                        var pollPacket = (ArtPollReply)packet;
                        handlePollReplyReceived(pollPacket, ip, mac);
                        if (OnPollReplyReceived != null) {
                            OnPollReplyReceived(pollPacket, ip, mac);
                        }
                        break;
                    case ArtPacket.OpTodData:
                        if (currArtPacketType != ArtPacketType.OpTodRequest && currArtPacketType != ArtPacketType.OpTodControl) {
                            break;
                        }
                        isValid = true;
                        var todPacket = (ArtTodData)packet;
                        handleToDDataReceived(todPacket, ip, mac);
                        if (OnTodDataReceived != null) {
                            OnTodDataReceived(todPacket, ip, mac);
                        }
                        break;
                    case ArtPacket.OpRDM:
                        if (currArtPacketType != ArtPacketType.OpRDM) {
                            break;
                        }
                        isValid = true;
                        var rdmPacket = (ArtRDM)packet;
                        if (OnRdmReceived != null) {
                            OnRdmReceived(rdmPacket, ip, mac);
                        }
                        //sendPending = false;
                        break;
                    case ArtPacket.OpPoll:
                    case ArtPacket.OpOutput:
                    case ArtPacket.OpTodRequest:
                    case ArtPacket.OpTodControl:
                    case ArtPacket.OpRDMSub:
                    default:
                        break;
                }
                if (!isValid) {
                    return;
                }
                //sendPending = false;
                readArtPacket = packet;
            } catch (Exception e) {
                Console.WriteLine("[ArtNetCore]:HandleArtNetMessage:{0}", e.Message);
            }

        }

        private byte[] buildRDMPacket(Catergory pid, CommandType cc, string UID, RDMParameters para = null) {
            if (para == null) {
                para = new RDMParameters(UID);
            }

            return rdmCore.RDMBuilder((ushort)pid, (byte)cc, para);
        }

        private void handlePollReplyReceived(ArtPollReply replyPacket, string ip, string mac) {
            lock (artDevListLock) {
                int portcount = replyPacket.channelNum;
                for (int i = 0; i < portcount; i++) {
                    ArtPortInfo info = new ArtPortInfo();
                    info.ip = ip;
                    info.mac = mac;
                    info.bindindex = replyPacket.bindIndex;
                    info.net = replyPacket.ArtAddrNet;
                    info.address = (byte)(((replyPacket.ArtAddress) | (replyPacket.outputAddr[i] & 0x0f)) & 0xff);
                    info.UIDList = null;
                    if (replyPacket.nodeReportArr != null) {
                        var arr = replyPacket.nodeReportArr;
                        info.nodeReport = new byte[arr.Length];
                        Array.Copy(arr, 0, info.nodeReport, 0, arr.Length);
                    }

                    if (!ArtDevList.ContainsKey(info.net)) {
                        ArtDevList.Add(info.net, new List<ArtPortInfo>());
                    }

                    bool addflag = true;
                    foreach (ArtPortInfo portinfo in ArtDevList[info.net]) {
                        if (portinfo.address == info.address) {
                            addflag = false;
                            break;
                        }
                    }

                    if (addflag) {
                        ArtDevList[info.net].Add(info); // 更新Universe列表
                    }
                } 
            }
        }

        private void handleToDDataReceived(ArtTodData dataPacket, string ip, string mac)
        {
            lock (artDevListLock)
            {
                if ((dataPacket.universe < 0) | (dataPacket.universe > 32767))
                    return;

                int i = dataPacket.universe;

                ArtPortInfo info = ArtDevList_uu[i];
                int datalen = dataPacket.UIDTotal;
                int blockCount = dataPacket.BlockCount;
                if (blockCount == 0)
                {
                    info.UIDList = new string[datalen];
                }
                for (int j = 0; j < dataPacket.UIDCount; j++)
                {
                    byte[] UIDByte = new byte[6];
                    Tools.CopyByte(ref UIDByte, dataPacket.ToD, 6, 0, 6 * j);
                    info.UIDList[blockCount * 200 + j] = Tools.Byte2HexString(UIDByte).Replace("\n", "");
                }
            }
        }

        private object getArtRDMReply(ArtRDM packet) {
            var obj = rdmCore.RDMResponseHandle(packet.RDMData);
            return obj;
        }

        private void requeryRDMProcess() {
            while (true) {
                if (BaseArtDevDetailList == null || BaseArtDevDetailList.DetailList == null && !addrMapEnable) { //无基准且无异形时跳过
                    Thread.Sleep(requeryInterval);
                    continue;
                }

                var currSubNum = getSubControlNum();
                var totalSubNum = 1;
                if (BaseArtDevDetailList != null && BaseArtDevDetailList.DetailList != null) { //有基准则以基准设备链分控数为准
                    totalSubNum = BaseArtDevDetailList.DetailList.Count;
                } else if (addrMapEnable) { //无基准则以异形为准
                    totalSubNum = getSubControlNumInLwf();
                }

                totalSubNum = Math.Max(1, totalSubNum);  
                var percent = currSubNum * 1.0f / totalSubNum;
                var isExceed = false;
                if (percent < ArtSubQueryThreshold) {
                    requeryOnce = false;
                    isExceed = false;
                } else {
                    isExceed = true;
                }
                if (requeryTime < 1) {
                    isExceed = true;    // 启动Agent自动重检测
                }
                if (isExceed && !requeryOnce) { // 只有第一次当前分控数超过阈值才进行重新查询
                    Thread.Sleep(ArtSubQueryDelaySec * 1000);
                    requeryTime++;
                    try {
                        RestartQueryUnvUidList();
                        requeryOnce = true;
                        requeryInfoTold = false;
                    } catch (System.Exception ex) {
                        typeLog(ex.ToString());
                    }
                }
                Thread.Sleep(requeryInterval);
            }
        }

        private void printArtSubUnvUIDList() {
            var dic = GetArtSubUnvUIDList();
            var str = "";
            if (dic != null) {
                foreach (var subDetail in dic) {
                    var sub = subDetail.Key;
                    var detail = subDetail.Value;
                    str += string.Format("Sub ID: {0} \r\n", sub);
                    if (detail == null) {
                        continue;
                    }
                    foreach (var portDetail in detail) {
                        var unv = portDetail.Key;
                        var uidList = portDetail.Value;
                        if (uidList == null) {
                            str += string.Format("Unv: {0}, Uid List: Null ", unv);
                        } else {
                            str += string.Format("Unv: {0}, Uid Count: {1}", unv, uidList.Count);
                            foreach (var uid in uidList) {
                                str += string.Format(" [{0}],", uid);
                            }
                        }
                        str += "\r\n";
                    }
                }
                typeLog(str, LogFile.Debug);
            }
        }

#if false //暂时不用
        #region ArtNet回复 - 线程方法

        // 生成ArtPollReply包
        private class ReplyPollPrars {
            public string mac;
            public string ip;

            public ReplyPollPrars(string ip, string mac) {
                this.ip = ip;
                this.mac = mac;
            }
        }
        private void genReplyPoll(object para) {
            ReplyPollPrars paras = (ReplyPollPrars)para;
            //string tempip = device.getIP();
            //string tempMac = device.mac;
            //device.setIP("192.168.1.121");
            //device.mac = "00e04c9a0082";
            for (int m = 0; m < netDeviceList.Count; m++) {
                var device = netDeviceList[m];
                if (!device.activeStatus) {
                    continue;
                }
                // 第一个包，端口1234
                ArtPollReply reply = new ArtPollReply();
                reply.ip = Tools.IP2Byte(device.getIP());
                reply.mac = Tools.String2Bytes(device.mac);
                reply.bindip = Tools.IP2Byte(device.getIP());
                reply.bindIndex = 1;
                reply.ArtAddrNet = NetInfo;
                reply.ArtAddress = SubNetInfo;
                reply.OEM = 0x0340;
                reply.VersionInfo = 0x020e;
                //reply.shortName = shortName;
                //reply.longName = longName;
                reply.nodeReport = "";
                reply.channelNum = 4;
                for (int i = 0; i < 4; i++) {
                    reply.channelStates[i] = new ChannelState(false, true);
                    reply.inputStates[i] = new ArtInputState(false);
                    reply.outputStates[i] = new ArtOutputState();
                    reply.inputAddr[i] = 0;
                    reply.outputAddr[i] = AddrInfo[i];
                }

                byte[] replymessage = reply.generatePacket();
                device.packetSender.sendArtNetPacket(paras.ip, paras.mac, replymessage.Length, replymessage);
                //packetSendFormEvent("ArtPollReply", device.getIP(), device.mac, new Dictionary<string, string>());

                //第二个包，端口5678
                reply = new ArtPollReply();
                reply.ip = Tools.IP2Byte(device.getIP());
                reply.mac = Tools.String2Bytes(device.mac);
                reply.bindip = Tools.IP2Byte(device.getIP());
                reply.bindIndex = 0;
                reply.ArtAddrNet = NetInfo;
                reply.ArtAddress = SubNetInfo;
                reply.OEM = 0x0340;
                reply.VersionInfo = 0x020e;
                //reply.shortName = shortName;
                //reply.longName = longName;
                reply.nodeReport = "";
                reply.channelNum = 4;
                for (int i = 0; i < 4; i++) {
                    reply.channelStates[i] = new ChannelState(false, true);
                    reply.inputStates[i] = new ArtInputState(false);
                    reply.outputStates[i] = new ArtOutputState();
                    reply.inputAddr[i] = 0;
                    reply.outputAddr[i] = AddrInfo[i + 4];
                }

                replymessage = reply.generatePacket();
                device.packetSender.sendArtNetPacket(paras.ip, paras.mac, replymessage.Length, replymessage);
                //packetSendFormEvent("ArtPollReply", device.getIP(), device.mac, new Dictionary<string, string>()); 
            }

            //device.setIP(tempip);
            //device.mac = tempMac;
        }

        // 生成ArtTodData包
        private void genReplyTod() {
            for (int m = 0; m < netDeviceList.Count; m++) {
                var device = netDeviceList[m];
                if (!device.activeStatus) {
                    continue;
                }
                for (int i = 0; i < ChannelCount; i++) {
                    UidLists[i] = rdmCores[i].GetUIDList();
                    int total = UidLists[i].Count;
                    int rep = total / 200;
                    // 超过200的情况下，发送多个包
                    for (int j = 0; j < rep; j++) {
                        ArtTodData reply = new ArtTodData(200);
                        reply.Net = NetInfo;
                        reply.Address = (byte)((SubNetInfo | AddrInfo[i]) & 0xff);
                        reply.PhyPort = (byte)(i + 1);
                        reply.BindIndex = 0;

                        reply.CommandResponse = 0;
                        reply.UIDTotal = total;
                        reply.UIDCount = 200;
                        reply.BlockCount = j;
                        for (int k = 0; k < 200; k++) {
                            Tools.CopyByte(ref reply.ToD, Tools.String2Bytes(UidLists[i][200 * j + k]), 6, 6 * k);
                        }

                        byte[] replymessage = reply.generatePacket();
                        device.packetSender.sendArtNetPacket(device.BroadcastIP, device.BroadcastMAC, replymessage.Length, replymessage);
                        //packetSendFormEvent("ArtTodData", device.getIP(), device.mac, new Dictionary<string, string>());
                    }
                    int fin = total % 200;
                    ArtTodData replyfin = new ArtTodData(fin);
                    replyfin.Net = NetInfo;
                    replyfin.Address = (byte)((SubNetInfo | AddrInfo[i]) & 0xff);
                    replyfin.PhyPort = (byte)(i + 1);
                    replyfin.BindIndex = 0;

                    replyfin.CommandResponse = 0;
                    replyfin.UIDTotal = total;
                    replyfin.UIDCount = fin;
                    replyfin.BlockCount = rep;
                    for (int k = 0; k < fin; k++) {
                        Tools.CopyByte(ref replyfin.ToD, Tools.String2Bytes(UidLists[i][200 * rep + k]), 6, 6 * k);
                    }

                    byte[] replyfinmessage = replyfin.generatePacket();
                    device.packetSender.sendArtNetPacket(device.ip, device.BroadcastMAC, replyfinmessage.Length, replyfinmessage);
                    //packetSendFormEvent("ArtTodData", device.getIP(), device.mac, new Dictionary<string, string>());
                }
            }
        }

        // 生成RDM包
        private class ReplyRDMParas {
            public int target;
            public byte[] rdm;
            public string target_ip;
            public string target_mac;

            public ReplyRDMParas(int target, byte[] rdm, string target_ip, string target_mac) {
                this.target = target;
                this.rdm = new byte[rdm.Length];
                Tools.CopyByte(ref this.rdm, rdm, rdm.Length);
                this.target_ip = target_ip;
                this.target_mac = target_mac;
            }
        }

        private void genReplyRDM(object para) {
            ReplyRDMParas paras = (ReplyRDMParas)para;
            byte[] rdmreply = rdmCores[paras.target].RDMHandle(paras.rdm);
            ArtRDM reply = new ArtRDM(paras.rdm);
            reply.Net = NetInfo;
            reply.Address = (byte)((SubNetInfo | AddrInfo[paras.target]) & 0xff);

            byte[] replymessage = reply.generatePacket();
            for (int m = 0; m < netDeviceList.Count; m++) {
                var device = netDeviceList[m];
                if (!device.activeStatus) {
                    continue;
                }
                device.packetSender.sendArtNetPacket(paras.target_ip, paras.target_mac, replymessage.Length, replymessage);
            }
            //packetSendFormEvent("ArtRDM", device.getIP(), device.mac, new Dictionary<string, string>());
        }

        #endregion

        #region ArtNet消息处理
        // 处理ArtPoll消息
        private Dictionary<string, string> HandleArtPollMessage(ArtPoll message, string ip, string mac) {
            Dictionary<string, string> detail = message.generateDetailDict();

            ReplyPollPrars pollparas = new ReplyPollPrars(ip, mac);
            new Thread(genReplyPoll).Start(pollparas);
            return detail;
        }
        #endregion 
#endif

        #endregion

        #endregion

        #region 对外功能接口

        #region 单例模式

        // 单例模式, 获取 BaseCoreNet 类实例
        public static BaseCoreNet getInstance(bool normalLogEnable, bool testLogEnable, bool detectNetLogEnable, bool debugLogEnable, List<string> ignoreMacList, List<string> effectMacList)
		{
			if (baseCoreNetUnique == null)
			{
                baseCoreNetUnique = new BaseCoreNet(normalLogEnable, testLogEnable, detectNetLogEnable, debugLogEnable, ignoreMacList, effectMacList);
				return baseCoreNetUnique;
			}
			else
			{
				return baseCoreNetUnique;
			}
		}

		// 销毁 BaseCoreNet 对象
		public void Dispose()
		{
			// 后台检测使能清除
			topologyDetectEnable = false;

			// 停止接收器，并关闭网卡设备
			closeAllDevices();
		}

		// 查询是否存在重大错误信息（底层 winpcap 不可用）
		public string getFatalMessage()
		{
			return fatalMessage;
		}

		#endregion

		#region 自动发送帧包 + [可选]分控配置包 + 显示设置包

		// 获取帧包发包参数
		public FramePacketPara getFramePacketPara()
		{
			return this.framePacketPara;
		}

		// 设置帧包发包参数
		public void setFramePacketPara(FramePacketPara p)
		{
			if (p != null)
			{
				this.framePacketPara = p;
				updateParaObjs();
			}
		}

		// 获取显示设置包发包参数
		public DispConfigPacketPara getDispConfigPacketPara()
		{
			return this.dispConfigPacketPara;
		}

		// 设置显示设置包发包参数
		public void setDispConfigPacketPara(DispConfigPacketPara d)
		{
			if (d != null)
			{
				this.dispConfigPacketPara = d;
				updateParaObjs();
			}
		}

		// 更新发包参数对象接口
		public void updateParaObjs()
		{
			for (int k = 0; k < netDeviceList.Count; k++)
			{
				NetDevice dev = (NetDevice)netDeviceList[k];
				dev.setParaObjs(framePacketPara, dispConfigPacketPara);
			}
		}

		// 读取并设置分控配置数据
		public void setSubConfigPacketData(string subFilePath)
		{
			typeLog("[BaseCoreNet] setSubConfigPacketData, subFilePath = " + subFilePath);

			List<byte[]> subConfigData = new List<byte[]>();
			FileStream fs = null;
			try
			{
				for (int i = 0; i < 10; i++)
				{
					subConfigData.Add(new byte[256]);
				}

				fs = File.OpenRead(subFilePath);

				fs.Position = 0x10100;
				fs.Read(subConfigData[0], 0, 256);		// 读0x10100~0x101ff

				fs.Position = 0x11000;
				fs.Read(subConfigData[1], 0, 256);		// 读0x11000~0x110ff

				fs.Position = 0x13000;
				fs.Read(subConfigData[2], 0, 256);		// 读0x13000~0x130ff
				fs.Position = 0x13100;
				fs.Read(subConfigData[3], 0, 256);		// 读0x13100~0x131ff

				fs.Position = 0x14000;
				fs.Read(subConfigData[4], 0, 256);		// 读0x14000~0x140ff
				fs.Position = 0x14100;
				fs.Read(subConfigData[5], 0, 256);		// 读0x14100~0x141ff

				fs.Position = 0x17000;
				fs.Read(subConfigData[6], 0, 256);		// 读0x17000~0x170ff
				fs.Position = 0x17100;
				fs.Read(subConfigData[7], 0, 256);		// 读0x17100~0x171ff
				fs.Position = 0x17200;
				fs.Read(subConfigData[8], 0, 256);		// 读0x17200~0x172ff

				fs.Position = 0x18000;
				fs.Read(subConfigData[9], 0, 256);		// 读0x18000~0x180ff

				setSubConfigPacketData(subConfigData);

			}
			catch (Exception ex)
			{
				typeLog("[Error] " + ex.Message + ex.StackTrace);
			}
			finally
			{
				if (fs != null)
				{
					fs.Close();
				}
			}
		}

		// 设置分控配置数据, 10 * 256 bytes
		public void setSubConfigPacketData(List<byte[]> allData)
		{
			for (int k = 0; k < netDeviceList.Count; k++)
			{
				NetDevice dev = (NetDevice)netDeviceList[k];
				dev.packetSender.setSubConfigPacketData(allData);
			}
		}

		// 设置分控配置数据周期发送使能
		public void setSubConfigSendEnable(bool enable)
		{
			subConfigSendEnable = enable;
			for (int k = 0; k < netDeviceList.Count; k++)
			{
				NetDevice dev = (NetDevice)netDeviceList[k];
				dev.packetSender.setSubConfigSendEnable(enable);
			}
		}

		// 获取分控配置数据周期发送使能
		public bool getSubConfigSendEnable()
		{
			return subConfigSendEnable;
		}

		#endregion

		#region 在线检测：启动、停止、查询

		// 启动后台定时检测 - 检测活动网口的控制设备链, 带有检测模式参数
		public void startTopologyListTest(DetectMode mode)
		{
			detectMode = mode;
			startTopologyListTest();
		}

		// 启动后台定时检测 - 检测活动网口的控制设备链
		public void startTopologyListTest()
		{
			if (sendBlackDataBeforeStartTopoDetect == true)
			{
				Thread.Sleep(50);

				// 发送 4 帧截图区域大小的黑数据
				int x = captureSize.X;
				int y = captureSize.Y;
				byte[] frameBuffer_RGB = new byte[x * y * 3];

				typeLogEnable = false;
				for (int k = 0; k < netDeviceList.Count; k++)
				{
					NetDevice dev = (NetDevice)netDeviceList[k];
					sendOneFrameFromBuffer(dev, frameBuffer_RGB, x * 3, y);
					sendOneFrameFromBuffer(dev, frameBuffer_RGB, x * 3, y);
					sendOneFrameFromBuffer(dev, frameBuffer_RGB, x * 3, y);
					sendOneFrameFromBuffer(dev, frameBuffer_RGB, x * 3, y);
				}
				typeLogEnable = true;
			}

			// 后台检测使能置位
			topologyDetectEnable = true;
		}

		// 设置后台检测使能标志位
		public void setTopologyDetectEnable(bool enable)
		{
			topologyDetectEnable = enable;
		}

		// 查询单个网口的活跃状态
		public bool getDeviceActiveStatus(int devIndex)
		{
			if (devIndex > netDeviceList.Count)
			{
				return false;
			}
			else
			{
				return ((NetDevice)netDeviceList[devIndex]).activeStatus;
			}
		}

		// 查询第一个活跃的网口
		public int getActiveDevIndex()
		{
			for (int i = 0; i < netDeviceList.Count; i++)
			{
				if (getDeviceActiveStatus(i) == true)
				{
					return i;
				}
			}

			return -1;
		}

		// 查询网口连接设备的拓扑结构链 - 字符串
		public string getDeviceTopologyChain()
		{
			if (currentOperFlag == -1)
			{
				return "";
			}

			string str = "wait_time_span = " + topologyDetect_Wait_ms + "\n";
			str += "maxSubCtrlNum = " + maxSubCtrlNum + ", maxEquipmentNum = " + maxEquipmentNum + "\n";
			for (int k = 0; k < netDeviceList.Count; k++)
			{
				NetDevice dev = (NetDevice)netDeviceList[k];
				string chainStr = "";
				if (currentOperFlag == 0)
				{
					// 目前后台操作 0 链, 从 1 链读取结果
					for (int i = 0; i < dev.totalCtrlNodeNum_1; i++)
					{
						ControlNode node = (ControlNode)(dev.ctrlNodeChain_1[i]);
						if (node != null)
						{
							chainStr += node.type;
						}
						else
						{
							chainStr += "N";
						}
					}
					str += String.Format("Device {0}: totalCtrlNodeNum = {1}, Chain = {2}\r\n", k, dev.totalCtrlNodeNum_1, chainStr, 0);
				}
				else if (currentOperFlag == 1)
				{
					// 目前后台操作 1 链, 从 0 链读取结果
					for (int i = 0; i < dev.totalCtrlNodeNum_0; i++)
					{
						ControlNode node = (ControlNode)(dev.ctrlNodeChain_0[i]);
						if (node != null)
						{
							chainStr += node.type;
						}
						else
						{
							chainStr += "N";
						}
					}
					str += String.Format("Device {0}: totalCtrlNodeNum = {1}, Chain = {2}\r\n", k, dev.totalCtrlNodeNum_0, chainStr, 1);
				}
			}

			return str;
		}

		// 查询网口连接设备的拓扑结构链 - 内部数据结构
        public List<ControlNode> getControlNodeList() 
        {
            List<ControlNode> nodeList = new List<ControlNode>();

            if (currentOperFlag == -1)
            {
            }
            else if (currentOperFlag == 0) 
            {	//目前后台操作 0 链, 从 1 链读取结果
                currentReadFlag = 1;
                for (int k = 0; k < netDeviceList.Count; k++) 
                {
                    NetDevice dev = (NetDevice)netDeviceList[k];
                    ArrayList alKeys = new ArrayList(dev.ctrlNodeChain_1.Keys);
                    //Console.WriteLine("currentReadFlag = 1, alKeys.Count = " + alKeys.Count);
                    alKeys.Sort();
                    foreach (int key in alKeys)
                    {
                        ControlNode node = (ControlNode)(dev.ctrlNodeChain_1[key]);
                        if (node != null)
                        {
                            ControlNode returnNode = ControlNode.Clone(node, true);
                            nodeList.Add(returnNode);
                            //for (int n = 0; n < node.brother.Count; n++)
                            //{
                            //    nodeList.Add(ControlNode.Clone(node.brother[n], false));
                            //}
                        }
                    }
                }
            }
            else if (currentOperFlag == 1)
            {	// 目前后台操作 1 链, 从 0 链读取结果
                currentReadFlag = 0;
                for (int k = 0; k < netDeviceList.Count; k++)
                {
                    NetDevice dev = (NetDevice)netDeviceList[k];
                    ArrayList alKeys = new ArrayList(dev.ctrlNodeChain_0.Keys);
                    //Console.WriteLine("currentReadFlag = 1, alKeys.Count = " + alKeys.Count);
                    alKeys.Sort();
                    foreach (int key in alKeys)
                    {
                        ControlNode node = (ControlNode)(dev.ctrlNodeChain_0[key]);
                        if (node != null)
                        {
                            ControlNode returnNode = ControlNode.Clone(node, true);
                            nodeList.Add(returnNode);
                            //for (int n = 0; n < node.brother.Count; n++)
                            //{
                            //    nodeList.Add(ControlNode.Clone(node.brother[n], false));
                            //}
                        }
                    }
                }
            }
            currentReadFlag = -1;

            return nodeList;
        }

		// 查询当前的分控数量
		public int getSubControlNum()
		{
			List<ControlNode> controlNodeList = getControlNodeList();
			if (controlNodeList == null)
			{
				return 0;
			}
			else
			{
				int num = 0;
				foreach (ControlNode node in controlNodeList)
				{
					if (node.type == 1)	// 判断分控
					{
						num++;
					}
				}
				return num;
			}
		}

		// 查询网口列表信息
		public List<NetDeviceInfo> getNetDeviceInfoList()
		{
			List<NetDeviceInfo> list = new List<NetDeviceInfo>();

			for (int k = 0; k < netDeviceList.Count; k++)
			{
				NetDevice dev = (NetDevice)netDeviceList[k];
				NetDeviceInfo info = new NetDeviceInfo(dev);
				list.Add(info);
			}

			return list;
		}

		// 查询网口列表信息 - 仅加载静态的网口信息
		public List<NetDeviceInfo> getStaticNetPortList()
		{
			List<NetDeviceInfo> infoList = new List<NetDeviceInfo>();

			try
			{
				var devices = CaptureDeviceList.Instance;	// 获取网络设备
				if (devices.Count < 1)
				{
					string noNetDevice = "本机找不到网络设备";
					if (LanguageOper.available == true)
					{
						noNetDevice = LanguageOper.ReadValue(section, "logStr.0");
					}
					typeLog(noNetDevice);
					return infoList;
				}
				else
				{
					for (int i = 0; i < devices.Count; i++)
					{
						NetDeviceInfo devInfo = new NetDeviceInfo();

						WinPcapDevice dev = (WinPcapDevice)devices[i];
						string disString = "";
						try
						{
							if (dev.Interface == null)
							{
								typeLog("[Error] dev " + i + " Interface is NULL");
							}
							else if (dev.Interface.MacAddress == null)
							{
								typeLog("[Error] dev " + i + " MacAddress is NULL");
							}
							else
							{
								disString = String.Format("{0} | MAC: {1}", dev.Interface.FriendlyName.PadRight(6, ' '), dev.Interface.MacAddress.ToString());
								devInfo.mac = dev.Interface.MacAddress.ToString();
							}
						}
						catch (Exception e)
						{
							typeLog("[Error] " + e.Message + ", " + e.StackTrace);
						}

						string nameStr = string.Format("网卡 {0}: {1}", i, disString);
						if (LanguageOper.available == true)
						{
							nameStr = string.Format(LanguageOper.ReadValue(section, "str.0"), i, disString);
						}

						string name = nameStr;
						devInfo.name = name;

						infoList.Add(devInfo);
					}
				}
			}
			catch (Exception e)
			{
				typeLog("[Error] " + e.Message + "\n" + e.StackTrace);
				infoList.Clear();
			}

			return infoList;
		}

		// 查询单个分控的版本
		public string getSubControlVersion(int devIndex, int subIndex)
		{
			string version = "";

			byte[] memData = new byte[256];

			bool timeout = readSubMem(devIndex, subIndex, 0x0, ref memData);

			if (timeout == true)
			{
				return "";
			}
			else
			{
				int headLength = 0;				// 包头数据长度, 已剥离
				int startIndex = 0x000040;		// 通信应答包 0x40~0x46 区段数据
				System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();

				byte[] byteArray = new byte[] { (byte)memData[headLength + startIndex + 0] };
				string funcType = asciiEncoding.GetString(byteArray);					// 系统功能分类
				byteArray = new byte[] { (byte)memData[headLength + startIndex + 1] };
				string subFuncType = asciiEncoding.GetString(byteArray);				// 系统功能子类
				int solutionMainType = memData[headLength + startIndex + 2];			// 解决方案主类型
				int solutionSubType = memData[headLength + startIndex + 3];				// 解决方案子类型
				byteArray = new byte[] { (byte)memData[headLength + startIndex + 4] };
				string appType = asciiEncoding.GetString(byteArray);					// 应用类型
				int projMainType = memData[headLength + startIndex + 5];				// 逻辑工程主版本
				int projSubType = memData[headLength + startIndex + 6];					// 逻辑工程子版本

				string firmwareVersion = funcType + subFuncType + solutionMainType + solutionSubType.ToString().PadLeft(2, '0')
					 + "_" + appType + projMainType.ToString().PadLeft(2, '0') + "_X" + projSubType.ToString().PadLeft(2, '0');

				version = firmwareVersion;
			}

			return version;
		}

		// 查询单个分控的应用类型
		public string getSubControlAppType(int devIndex, int subIndex)
		{
			string appType = "";

			byte[] memData = new byte[256];

			bool timeout = readSubMem(devIndex, subIndex, 0x0, ref memData);

			if (timeout == true)
			{
				return "";
			}
			else
			{
				int headLength = 0;				// 包头数据长度, 已剥离
				int startIndex = 0x000040;		// 通信应答包 0x40~0x46 区段数据
				System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();

				byte[] byteArray = new byte[] { (byte)memData[headLength + startIndex + 4] };
				appType = asciiEncoding.GetString(byteArray);	// 应用类型
			}

			return appType;
		}

		// 查询是否有网口连接了设备
		public bool getSubEquActiveState()
		{
			for (int tryTimes = 0; tryTimes < 3; tryTimes++)
			{
				for (int k = 0; k < netDeviceList.Count; k++)
				{
					if (getDeviceActiveStatus(k) == true)
					{
						return true;
					}
				}
			}
			return false;
		}

		// 发送清理命令（全局），保证所有分控的总包数统一
		public void sendClearPacketCommand(int devIndex)
		{
			if (devIndex > -1)
			{
				byte[] openLoadData = new byte[256];
				openLoadData[0] = 0x00;
				writeSubMem(devIndex, 0, 0x0, openLoadData, true);
			}
		}

        public void setDetectType(DetectType type) {
            this.detectType = type;
        }

        public DetectType getDetectType() {
            return detectType;
        }

		#endregion

		#region 截屏与下发显示数据使能

		// 启动截屏下发
		public void startCaptureAndSend()
		{
			captureSendState = CaptureSendState.Run;
            StartFrameCountTimer();
		}

		// 停止截屏下发
		public void stopCaptureAndSend()
		{
			captureSendState = CaptureSendState.Stop;
            StopFrameCountTimer();
		}

		// 暂停截屏下发
		public void pauseCaptureAndSend()
		{
			if (captureSendState == CaptureSendState.Run)
			{
				captureSendState = CaptureSendState.Pause;
			}
		}

		// 恢复截屏下发
		public void resumeCaptureAndSend()
		{
			if (captureSendState == CaptureSendState.Pause)
			{
				captureSendState = CaptureSendState.Run;
			}
		}

		// 改变截屏区域的位置及大小
		public bool changeCaptureLocationAndSize(int x, int y, int width, int height)
		{
			if (x < 0 || y < 0 || width < 0 || height < 0)
			{
				return false;
			}
			else if (x == captureLocation.X && y == captureLocation.Y && width == captureSize.X && height == captureSize.Y)
			{
				return true;
			}
			else
			{
				typeLog(String.Format("Change Capture Area, x = {0}, y = {1}, width = {2}, height = {3}", x, y, width, height));

				captureLocation = new Point(x, y);
                int screenWidth = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Width;
                int screenHeight = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height;
                if (width > screenWidth) width = screenWidth;
                if (height > screenHeight) height = screenHeight;
				captureSize = new Point(width, height);

				buildAddrMapTable();	// 重新生成异形映射表

				return true;
			}
		}

		// 改变截屏下发的帧频
		public void changeCaptureSendFPS(int newFps)
		{
			if (fps != newFps)
			{
				fps = newFps;
				sleepTimeBase = 1000 / fps;
				typeLog(String.Format("Capture Fps Changed, fps = {0}", fps) + "     " + DateTime.Now.Ticks / 10000);
			}
		}

        public CaptureSendState GetCaptureSendState() {
            return captureSendState;
        }

		#endregion

		#region 读写分控及附属设备

		// 读分控 Mem - devIndex：网络设备索引，subIndex：分控级联深度（0-255），返回超时标记
		public bool readSubMem(int devIndex, int subIndex, uint memAddr, ref byte[] memData)
		{
			// 超时标记
			bool timeout = true;

			// 参数合法性检查
			bool checkResult = checkParaments(devIndex, subIndex, memAddr, memData, NodeType.SubCtrl);

			if (checkResult == true)
			{
				NetDevice dev = netDeviceList[devIndex];

				int packageId = 0;
				while (true)
				{
					readSubMemPackageId++;
					if (readSubMemPackageId >= readSubMemPackageIdMax)
					{
						readSubMemPackageId = readSubMemPackageIdMin;
					}

					packageId = readSubMemPackageId;

					if (dev.readSubReplyList[packageId].inUse == true)
					{
						continue;
					}
					else
					{
						dev.readSubReplyList[packageId].inUse = true;
						break;
					}
				}

				byte[] packet = dev.packetBuilder.buildCommandPacket4Read(InnerCommandTarget.SubCtrl, subIndex, packageId, (int)memAddr);

				dev.readSubReplyList[packageId].doneEvent.Reset();
				dev.packetSender.sendOneReadyPacket(packet);

				// 设置超时标记
				timeout = !(dev.readSubReplyList[packageId].doneEvent.WaitOne(readSubTimeout_ms));

				// 读操作未超时
				if (timeout == false)
				{
					Array.Copy(dev.readSubReplyList[packageId].replyData, memData, 256);
				}
				else
				{
					//typeLog("[Error] ReadSubMem, timeout = " + timeout + ", packageId = " + packageId);
				}

				dev.readSubReplyList[packageId].inUse = false;
			}
			else
			{
				typeLog("[Error] ReadSubMem, Wrong Parameters!");
			}

			return timeout;
		}

		// 写分控 Mem - devIndex：网络设备索引，subIndex：分控级联深度（0-255），memData：要写入的数据，overallFlag：全局操作标记
		public void writeSubMem(int devIndex, int subIndex, uint memAddr, byte[] memData, bool overallFlag)
		{
			// 参数合法性检查
			bool checkResult = checkParaments(devIndex, subIndex, memAddr, memData, NodeType.SubCtrl);

			if (subIndex == 0)
			{
				overallFlag = true;
			}

			if (checkResult == true)
			{
				NetDevice dev = netDeviceList[devIndex];

				writeSubMemPackageId++;
				if (writeSubMemPackageId >= writeSubMemPackageIdMax)
				{
					writeSubMemPackageId = writeSubMemPackageIdMin;
				}

				int packageId = writeSubMemPackageId;

				byte[] packet = dev.packetBuilder.buildCommandPacket4Write(InnerCommandTarget.SubCtrl, subIndex, packageId, (int)memAddr, memData, overallFlag);

				dev.packetSender.sendOneReadyPacket(packet);
			}
			else
			{
				typeLog("[Error] WriteSubMem, Wrong Parameters!");
			}

			Thread.Sleep(1);

			return;
		}

		// 读附属设备 Mem - devIndex：网络设备索引，equIndex：附属设备级联深度（0-63），返回超时标记
		public bool readEquMem(int devIndex, int equIndex, uint memAddr, ref byte[] memData)
		{
			// 超时标记
			bool timeout = true;

			// 参数合法性检查
			bool checkResult = checkParaments(devIndex, equIndex, memAddr, memData, NodeType.Equipment);

			if (checkResult == true)
			{
				NetDevice dev = netDeviceList[devIndex];

				int packageId = 0;
				while (true)
				{
					readEquMemPackageId++;
					if (readEquMemPackageId >= readEquMemPackageIdMax)
					{
						readEquMemPackageId = readEquMemPackageIdMin;
					}

					packageId = readEquMemPackageId;

					if (dev.readEquReplyList[packageId].inUse == true)
					{
						continue;
					}
					else
					{
						dev.readEquReplyList[packageId].inUse = true;
						break;
					}
				}

				byte[] packet = dev.packetBuilder.buildCommandPacket4Read(InnerCommandTarget.Equipment, equIndex, packageId, (int)memAddr);

				dev.readEquReplyList[packageId].doneEvent.Reset();
				dev.packetSender.sendOneReadyPacket(packet);

				// 设置超时标记
				timeout = !(dev.readEquReplyList[packageId].doneEvent.WaitOne(readEquTimeout_ms));

				// 读操作未超时
				if (timeout == false)
				{
					Array.Copy(dev.readEquReplyList[packageId].replyData, memData, 256);
				}
				else
				{
					typeLog("[Error] ReadEquMem, timeout = " + timeout + ", packageId = " + packageId);
				}

				dev.readEquReplyList[packageId].inUse = false;
			}
			else
			{
				typeLog("[Error] ReadEquMem, Wrong Parameters!");
			}

			return timeout;
		}

		// 写附属设备 Mem - devIndex：网络设备索引，equIndex：附属设备级联深度（1-63），memData：要写入的数据，overallFlag：全局操作标记
		public void writeEquMem(int devIndex, int equIndex, uint memAddr, byte[] memData, bool overallFlag)
		{
			// 参数合法性检查
			bool checkResult = checkParaments(devIndex, equIndex, memAddr, memData, NodeType.SubCtrl);

			if (equIndex == 0)
			{
				overallFlag = true;
			}

			if (checkResult == true)
			{
				NetDevice dev = netDeviceList[devIndex];

				writeEquMemPackageId++;
				if (writeEquMemPackageId >= writeEquMemPackageIdMax)
				{
					writeEquMemPackageId = writeEquMemPackageIdMin;
				}

				int packageId = writeEquMemPackageId;

				byte[] packet = dev.packetBuilder.buildCommandPacket4Write(InnerCommandTarget.Equipment, equIndex, packageId, (int)memAddr, memData, overallFlag);

				dev.packetSender.sendOneReadyPacket(packet);
			}
			else
			{
				typeLog("[Error] WriteEquMem, Wrong Parameters!");
			}

			return;
		}

        // 令分控工作在强制百兆模式
        public void setSubMegaByteMode(int subIndex, NetSpeed speed) {
            var activeDeviceIndex = getActiveDevIndex();
            var data = new byte[256];
            data[0] = (byte)speed;
            var overall = false;
            if (subIndex == -1) {
                subIndex = 0;
                overall = true;
            }
            writeSubMem(activeDeviceIndex, subIndex, 0x20000200, data, overall);
        }

		#endregion

		#region 读写Flash

		// 操作集：发送定点写命令, 写入Flash - 定点
		public void write2Flash(int devIndex, int subControlDepth, byte[] data, int baseClearAddr, int baseRWAddr, int length, bool withProcess, DeviceType targetDevType = DeviceType.SubControl)
		{
			updateProcessValue = 0;
			WriteEventArgs writeArgs = new WriteEventArgs();

			if (length > data.Length)
			{
				length = data.Length;
			}

			int blockNum = (int)(length / blockSize);
			if (length % blockSize > 0)
			{
				blockNum++;
			}

			byte[] loadData_0 = new byte[256];
			byte[] loadData = new byte[256];
			for (int k = 0; k < 256; k++)
			{
				loadData_0[k] = 0x0;
				loadData[k] = 0x0;
			}

			uint currentClearAddr = (uint)baseClearAddr;
			uint currentRWAddr = (uint)baseRWAddr;

			if (withProcess)
			{
				updateProcessValue = 0;
				writeArgs.processValue = 0;
			}
            
			/*********************************** 写入数据 *****************************************/

			// 数据长度 length <= 256
			if (length <= 256)
			{
				// 擦除 block
				writeSubMem(devIndex, subControlDepth, currentClearAddr, loadData_0, false);
				Thread.Sleep(eraseTimeSpan);

				// 发送数据包
				Array.Copy(data, loadData, length);
				writeSubMem(devIndex, subControlDepth, currentRWAddr, loadData, false);

				Thread.Sleep(updateTimeSpan);
			}
			// 数据长度 length > 256
			else
			{
				int packetNum = length / 256;
				int clearGap = (int)(blockSize / 256);

				/************************** 耗时操作 **************************/
				for (int i = 0; i < packetNum; i++)
				{
					if (forceStop)
					{
						updateProcessValue = 0;
						writeArgs.processValue = 0;
						updateDoneNum = 0;
						return;
					}

					if (i % clearGap == 0)		// 擦除新 Block - 4KB
					{
						// 擦除 Block
						writeSubMem(devIndex, subControlDepth, currentClearAddr, loadData_0, false);
						currentClearAddr += blockSize;

						Thread.Sleep(eraseTimeSpan);
					}

					// 发送数据包
					Array.Copy(data, i * 256, loadData, 0, 256);
					if (targetDevType == DeviceType.SubControl)
					{
						writeSubMem(devIndex, subControlDepth, currentRWAddr, loadData, false);
					}
					else if (targetDevType == DeviceType.Equipment)
					{
						writeEquMem(devIndex, subControlDepth, currentRWAddr, loadData, false);
					}
					currentRWAddr += 256;

					Thread.Sleep(updateTimeSpan);

					// 触发进度更新事件
					if (withProcess)
					{
						updateProcessValue = i * 100 / packetNum;
						if (writeArgs.processValue != updateProcessValue)
						{
							if (WriteFlashStateUpdated != null)
							{
								writeArgs.processValue = updateProcessValue;
								WriteFlashStateUpdated(this, writeArgs);
							}
						}
					}
				}
				/************************** 耗时操作 **************************/

				if (length % 256 > 0)	// 末尾数据 - 目前数据已对齐, 无末尾数据
				{
					;
				}
			}

			/********************************* End 写入数据 ***************************************/

			// 触发进度更新事件
			if (withProcess)
			{
				updateProcessValue = 100;
				if (WriteFlashStateUpdated != null)
				{
					writeArgs.processValue = 100;
					WriteFlashStateUpdated(this, writeArgs);
				}
				updateDoneNum++;
			}
		}

		// 操作集：发送定点写命令, 写入Flash - 并行
		public void write2Flash_parallet(int devIndex, int[] depthList, byte[] data, int baseClearAddr, int baseRWAddr, int length, bool withProcess, DeviceType targetDevType = DeviceType.SubControl)
		{
			updateProcessValue = 0;
			WriteEventArgs writeArgs = new WriteEventArgs();

			int totalNum = depthList.Length;

			if (length > data.Length)
			{
				length = data.Length;
			}

			int blockNum = (int)(length / blockSize);
			if (length % blockSize > 0)
			{
				blockNum++;
			}

			byte[] loadData_0 = new byte[256];
			byte[] loadData = new byte[256];
			for (int k = 0; k < 256; k++)
			{
				loadData_0[k] = 0x0;
				loadData[k] = 0x0;
			}

			uint currentClearAddr = (uint)baseClearAddr;
			uint currentRWAddr = (uint)baseRWAddr;

			/*********************************** 并行写入数据 *****************************************/

			// 数据长度 length <= 256
			if (length <= 256)
			{
				// 擦除 Block
				if (targetDevType == DeviceType.SubControl)
				{
					for (int k = 0; k < totalNum; k++)
					{
						writeSubMem(devIndex, depthList[k], currentClearAddr, loadData_0, false);
					}
				}
				else
				{
					for (int k = 0; k < totalNum; k++)
					{
						writeEquMem(devIndex, depthList[k], currentClearAddr, loadData_0, false);
					}
				}

				Thread.Sleep(eraseTimeSpan);

				// 发送数据包
				Array.Copy(data, loadData, length);
				if (targetDevType == DeviceType.SubControl)
				{
					for (int k = 0; k < totalNum; k++)
					{
						writeSubMem(devIndex, depthList[k], currentRWAddr, loadData, false);
					}
				}
				else if(targetDevType == DeviceType.Equipment)
				{
					for (int k = 0; k < totalNum; k++)
					{
						writeEquMem(devIndex, depthList[k], currentRWAddr, loadData, false);
					}
				}

				Thread.Sleep(updateTimeSpan);
			}
			// 数据长度 length > 256
			else
			{
				int packetNum = length / 256;
				int clearGap = (int)(blockSize / 256);

				/************************** 耗时操作 **************************/
				for (int i = 0; i < packetNum; i++)
				{
					if (forceStop)
					{
						updateProcessValue = 0;
						updateDoneNum = 0;
						return;
					}

					if (i % clearGap == 0)		// 擦除新 Block - 4KB
					{
						// 擦除 Block
						if (targetDevType == DeviceType.SubControl)
						{
							for (int k = 0; k < totalNum; k++)
							{
								writeSubMem(devIndex, depthList[k], currentClearAddr, loadData_0, false);
							}
						}
						else if (targetDevType == DeviceType.Equipment)
						{
							for (int k = 0; k < totalNum; k++)
							{
								writeEquMem(devIndex, depthList[k], currentClearAddr, loadData_0, false);
							}
						}

						currentClearAddr += blockSize;

						Thread.Sleep(eraseTimeSpan);
					}

					// 发送数据包
					Array.Copy(data, i * 256, loadData, 0, 256);
					if (targetDevType == DeviceType.SubControl)
					{
						for (int k = 0; k < totalNum; k++)
						{
							writeSubMem(devIndex, depthList[k], currentRWAddr, loadData, false);
						}
					}
					else if (targetDevType == DeviceType.Equipment)
					{
						for (int k = 0; k < totalNum; k++)
						{
							writeEquMem(devIndex, depthList[k], currentRWAddr, loadData, false);
						}
					}

					currentRWAddr += 256;

					Thread.Sleep(updateTimeSpan);

					// 触发进度更新事件
					if (withProcess)
					{
						updateProcessValue = i * 100 / packetNum;
						if (writeArgs.processValue != updateProcessValue)
						{
							if (WriteFlashStateUpdated != null)
							{
								writeArgs.processValue = updateProcessValue;
								WriteFlashStateUpdated(this, writeArgs);
							}
						}
					}
				}
				/************************** 耗时操作 **************************/

				if (length % 256 > 0)	// 末尾数据 - 目前数据已对齐, 无末尾数据
				{
					;
				}
			}

			// 触发进度更新事件
			if (withProcess)
			{
				updateProcessValue = 100;
				if (WriteFlashStateUpdated != null)
				{
					writeArgs.processValue = 100;
					WriteFlashStateUpdated(this, writeArgs);
				}
			}

			/********************************* End 并行写入数据 ***************************************/
		}

		// 操作集：发送定点写命令, 写入Flash - 全局
		public void write2Flash_total(int devIndex, byte[] data, int baseClearAddr, int baseRWAddr, int length, bool withProcess, DeviceType targetDevType = DeviceType.SubControl)
		{
			updateProcessValue = 0;
			WriteEventArgs writeArgs = new WriteEventArgs();

			if (length > data.Length)
			{
				length = data.Length;
			}

			int blockNum = (int)(length / blockSize);
			if (length % blockSize > 0)
			{
				blockNum++;
			}

			byte[] loadData_0 = new byte[256];
			byte[] loadData = new byte[256];
			for (int k = 0; k < 256; k++)
			{
				loadData_0[k] = 0x0;
				loadData[k] = 0x0;
			}

			uint currentClearAddr = (uint)baseClearAddr;
			uint currentRWAddr = (uint)baseRWAddr;

			if (withProcess)
			{
				updateProcessValue = 0;
			}

			/*********************************** 全局写入数据 *****************************************/

			// 数据长度 length <= 256
			if (length <= 256)
			{
				// 擦除 Block - 全局操作
				if (targetDevType == DeviceType.SubControl)
				{
					writeSubMem(devIndex, 0, currentClearAddr, loadData_0, true);
				}
				else if (targetDevType == DeviceType.Equipment)
				{
					writeEquMem(devIndex, 0, currentClearAddr, loadData_0, true);
				}

				Thread.Sleep(eraseTimeSpan);

				// 发送数据包 - 全局操作
				Array.Copy(data, loadData, length);

				if (targetDevType == DeviceType.SubControl)
				{
					writeSubMem(devIndex, 0, currentRWAddr, loadData, true);
				}
				else if (targetDevType == DeviceType.Equipment)
				{
					writeEquMem(devIndex, 0, currentRWAddr, loadData, true);
				}

				Thread.Sleep(updateTimeSpan);
			}
			// 数据长度 length > 256
			else
			{
				int packetNum = length / 256;
				int clearGap = (int)(blockSize / 256);

				/************************** 耗时操作 **************************/
				for (int i = 0; i < packetNum; i++)
				{
					if (forceStop)
					{
						updateProcessValue = 0;
						updateDoneNum = 0;
						return;
					}

					if (i % clearGap == 0)		// 擦除新 Block - 4KB
					{
						// 擦除 Block - 全局操作
						if (targetDevType == DeviceType.SubControl)
						{
							writeSubMem(devIndex, 0, currentClearAddr, loadData_0, true);
						}
						else if (targetDevType == DeviceType.Equipment)
						{
							writeEquMem(devIndex, 0, currentClearAddr, loadData_0, true);
						}

						currentClearAddr += blockSize;

						Thread.Sleep(eraseTimeSpan);
					}

					// 发送数据包 - 全局操作
					Array.Copy(data, i * 256, loadData, 0, 256);
					if (targetDevType == DeviceType.SubControl)
					{
						writeSubMem(devIndex, 0, currentRWAddr, loadData, true);
					}
					else if (targetDevType == DeviceType.Equipment)
					{
						writeEquMem(devIndex, 0, currentRWAddr, loadData, true);
					}
					currentRWAddr += 256;

					Thread.Sleep(updateTimeSpan);

					// 触发进度更新事件
					if (withProcess)
					{
						updateProcessValue = i * 100 / packetNum;
						if (writeArgs.processValue != updateProcessValue)
						{
							if (WriteFlashStateUpdated != null)
							{
								writeArgs.processValue = updateProcessValue;
								WriteFlashStateUpdated(this, writeArgs);
							}
						}
					}
				}
				/************************** 耗时操作 **************************/

				if (length % 256 > 0)	// 末尾数据 - 目前数据已对齐, 无末尾数据
				{
					;
				}
			}

			// 触发进度更新事件
			if (withProcess)
			{
				updateProcessValue = 100;
				if (WriteFlashStateUpdated != null)
				{
					writeArgs.processValue = 100;
					WriteFlashStateUpdated(this, writeArgs);
				}
			}

			/********************************* End 全局写入数据 ***************************************/
		}

		// 批量读取 Flash 固件区
		public byte[] readFirmware(int devIndex, int subControlDepth, uint startReadAddr, int readLength, bool withProcess, DeviceType targetDevType = DeviceType.SubControl)
		{
			//byte[] replyData1 = netDeviceList[devIndex].readSubFirmware(subControlDepth, startReadAddr, readLength, withProcess);

			/**************************************************************************/

			ReadEventArgs readArgs = new ReadEventArgs();

			bool timeout = false;
			byte[] memData = new byte[256];
			byte[] replyData = new byte[readLength];

			for (int n = 0; n < readLength / 256; n++)
			{
				if (forceStop)
				{
					break;
				}

				if (withProcess)
				{
					int currentValue = n * 256 * 100 / readLength;
					if (readFlashProcessValue != currentValue)
					{
						readFlashProcessValue = currentValue;
						if (ReadFlashStateUpdated != null)
						{
							readArgs.processValue = readFlashProcessValue;
							ReadFlashStateUpdated(this, readArgs);
						}
					}
				}

				if (targetDevType == DeviceType.SubControl)
				{
					timeout = readSubMem(devIndex, subControlDepth, startReadAddr, ref memData);
				}
				else if (targetDevType == DeviceType.Equipment)
				{
					timeout = readEquMem(devIndex, subControlDepth, startReadAddr, ref memData);
				}

				if (timeout == false)
				{
					Array.Copy(memData, 0, replyData, n * 256, 256);
				}
				else
				{
					MessageBox.Show("ReadSubFirmware timeout!");
					break;
				}

				startReadAddr += 256;	// 起始地址向后偏移 256
			}

			if (withProcess && ReadFlashStateUpdated != null)
			{
				readArgs.processValue = 100;
				ReadFlashStateUpdated(this, readArgs);
			}

			/**************************************************************************/

			//int res = Tools.MemoryCompare(replyData1, replyData);

			return replyData;
		}

		#endregion

		#region 在线升级

		// 设置强制停止在线升级标志
		public void forceStopUpdate()
		{
			this.forceStop = true;
		}

		// 清除强制停止在线升级标志
		public void clearForceStopUpdate()
		{
			this.forceStop = false;
		}

		#endregion

		#region 通信平台参数设置

		// 获取当前内核版本
        public string getVersion() {
            var date = System.IO.File.GetLastWriteTime(this.GetType().Assembly.Location);
            var version = string.Format("BaseCoreNet: v{0} @ {1}.{2}.{3}", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(), date.Year, date.Month, date.Day);
            return version;
        }

		// 设置调试日志使能
		public void setDebugLogEnable(bool normalLogEnable, bool testLogEnable, bool detectLogEnable)
		{
			this.normalLogEnable = normalLogEnable;
			this.testLogEnable = testLogEnable;
			this.detectNetLogEnable = detectLogEnable;
		}

		// 设置内容发送方式： AllDevices / ActiveOnly / None
		public void setSendMode(string mode)
		{
			sendMode = mode;
		}

		// 设置回包等待时间
		public void setWaitTime_ms(int value_ms)
		{
			if (value_ms > 0)
			{
				topologyDetect_Wait_ms = value_ms;
			}
		}

		// 设置在线检测周期
		public void setTopologyDetect_TimeSpan_ms(int value_ms)
		{
			if (value_ms > 0)
			{
				topologyDetect_TimeSpan_ms = value_ms;
			}
		}

		// 设置最大分控数量、最大附属设备数量
		public void setMaxSubEquNum(int maxSubNum, int maxEquNum)
		{
			maxSubCtrlNum = maxSubNum;
			maxEquipmentNum = maxEquNum;
		}

		// 设置擦除时间间隔
		public void setEraseTimeSpan(int eraseTimeSpanNew)
		{
			if (eraseTimeSpanNew > 0)
			{
				this.eraseTimeSpan = eraseTimeSpanNew;
			}
		}

		// 设置发送升级包时间间隔
		public void setUpdateTimeSpan(int updateTimeSpanNew)
		{
			if (updateTimeSpanNew > 0)
			{
				this.updateTimeSpan = updateTimeSpanNew;
			}
		}

		// 设置分控/附属设备级联深度读取位置：54_56 / 59_61，默认 59_61
		public void setDepthReadPosition(string readPos)
		{
			if (readPos.Equals("54_56") || readPos.Equals("59_61"))
			{
				for (int k = 0; k < netDeviceList.Count; k++)
				{
					NetDevice dev = (NetDevice)netDeviceList[k];
					dev.packetReceiver.setDepthReadPosition(readPos);
				}
			}
		}

		// 设置节目亮度
		public void setProgramBrightness(int bright)
		{
			if (bright < 0 || bright > 255)
			{
				typeLog("[Error] Wrong Bright Value: " + bright);
				return;
			}

			DispConfigPacketPara d = getDispConfigPacketPara();
			if (d.Light_ALL != bright)
			{
				d.Light_ALL = bright;
				setDispConfigPacketPara(d);
				updateParaObjs();
				typeLog("[BaseCoreNet] Program Brightness Changed: bright = " + bright);
			}
		}

		// 设置是否使用 Socket 发送数据
		public bool setSendDataUseSocket(bool state)
		{
			useWinPcap = !state;
            bool success = false;
            for (int k = 0; k < netDeviceList.Count; k++)
            {
                NetDevice dev = (NetDevice)netDeviceList[k];

                dev.packetSender.setUseWinPcap(!state);
                success = true;
            }

            return success;
		}

		// 获取是否使用 Socket 发送数据
		public bool getSendDataUseSocket()
		{
			return !useWinPcap;
		}

		// 设置 socket 发包模式
		public void setSocketSendMode(SocketSendMode socketSendMode)
		{
			this.socketSendMode = socketSendMode;
			for (int k = 0; k < netDeviceList.Count; k++)
			{
				NetDevice dev = (NetDevice)netDeviceList[k];
				dev.packetSender.setSocketSendMode(socketSendMode);
			}
		}

		// 获取 socket 发包模式
		public SocketSendMode getSocketSendMode()
		{
			return socketSendMode;
		}

        // 设置网络速度配置
        public void SetNetSpeed(NetSpeed netSpeed)
        {
            this.netSpeed = netSpeed;
        }

        // 获取网络速度配置
        public NetSpeed GetNetSpeed()
        {
            return netSpeed;
        }

		#endregion

		#region 支持多路交换机

		// 设置是否支持交换机
		public void setUseSwitch(bool useSwitch)
		{
			foreach (NetDevice dev in netDeviceList)
			{
				dev.packetSender.setUseSwitch(useSwitch);
			}
			this.useSwitch = useSwitch;
			typeLog("[BaseCoreNet] setUseSwitch, useSwitch = " + useSwitch);
		}

		// 查询是否支持交换机
		public bool getUseSwitch()
		{
			return useSwitch;
		}

		// 【暂时停用】读取多路交换机配置文件
		public string readSwitchConfigFile(string configFilePath)
		{
			StreamReader reader = null;
			try
			{
				List<IdMacPair> tmpList = new List<IdMacPair>();

				if (macList == null)
				{
					macList = new List<string>();
				}
				else
				{
					macList.Clear();
				}

				// 读取配置文件
				reader = new StreamReader(configFilePath);
				string currentLine = null;
				Regex r = new Regex(@"^([0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F])$");
				while (true)
				{
					currentLine = reader.ReadLine();

					if (currentLine == null)
					{
						break;
					}
					else if (currentLine.Trim().Equals(""))
					{
						continue;
					}
					else if (currentLine.Trim().StartsWith("#"))
					{
						continue;
					}

					IdMacPair id_mac = new IdMacPair();
					string[] subStrs = currentLine.Split(',');

					id_mac.startId = int.Parse(subStrs[0].Trim());
				
					// 检查 MAC 地址的合法性
					if (r.IsMatch(subStrs[1].Trim().Replace("-", "").Replace(":", "")))
					{
						id_mac.mac = tmpList.Count.ToString();
					}
					else
					{
						string msg = string.Format("MAC地址格式不正确 ({0}), 请检查", subStrs[1].Trim());
						if (LanguageOper.available == true)
						{
							msg = string.Format(LanguageOper.ReadValue(section, "msgStr.1"), subStrs[1].Trim());
						}
						
						return msg;
					}

					tmpList.Add(id_mac);
					macList.Add(subStrs[1].Trim().Replace("-", "").Replace(":", ""));
				}

				// 按照 startId 排序
				tmpList.Sort(IdMacPairSortCompare);

				for (int i = 0; i < tmpList.Count - 1; i++)
				{
					tmpList[i].endId = tmpList[i + 1].startId - 1;
				}
				tmpList[tmpList.Count - 1].endId = maxSubEquNum;

				this.switchConfigFilePath = configFilePath;
				idMacList = tmpList;

				// 根据 idMacList 生成 "行映射表"、"分控映射表"、"附属设备映射表"
				bool res = makeMacTable();

				if (res == true)
				{
					foreach (NetDevice dev in netDeviceList)
					{
						dev.packetSender.makeMacTables(macList, rowMacTable, subMacTable, equMacTable);
					}
					return "success";
				}
				else
				{
					return "error";
				}
			}
			catch (Exception e)
			{
				typeLog("[Error] " + e.Message);
				return "error";
			}
			finally
			{
				if (reader != null)
				{
					reader.Close();
				}
			}
		}

		// 读取多路交换机配置文件 - Json 格式
		public string readSwitchConfigFileJson(string configFilePath)
		{
			try
			{
				List<IdMacPair> tmpList = new List<IdMacPair>();

				if (macList == null)
				{
					macList = new List<string>();
				}
				else
				{
					macList.Clear();
				}

				// 从 Json 格式的交换机配置文件中解析数据
				SwitchMapJson switchMapJson = parseSwitchConfigFromJson(configFilePath);

				// Json 解析过程存在错误
				if (switchMapJson == null)
				{
					return "error";
				}

				foreach (SwitchMapJsonUnit unit in switchMapJson.map)
				{
					IdMacPair id_mac = new IdMacPair();
					id_mac.startId = int.Parse(unit.SubIndex);
					id_mac.mac = tmpList.Count.ToString();

					tmpList.Add(id_mac);
					macList.Add(unit.Mac.Trim().Replace("-", "").Replace(":", ""));
				}

				// 按照 startId 排序
				tmpList.Sort(IdMacPairSortCompare);

				for (int i = 0; i < tmpList.Count - 1; i++)
				{
					tmpList[i].endId = tmpList[i + 1].startId - 1;
				}
				tmpList[tmpList.Count - 1].endId = maxSubEquNum;

				this.switchConfigFilePath = configFilePath;
				idMacList = tmpList;

				// 根据 idMacList 生成 "行映射表"、"分控映射表"、"附属设备映射表"
				bool res = makeMacTable();

				if (res == true)
				{
					foreach (NetDevice dev in netDeviceList)
					{
						dev.packetSender.makeMacTables(macList, rowMacTable, subMacTable, equMacTable);
					}

					typeLog("[BaseCoreNet] readSwitchConfigFileJson, configFilePath = " + configFilePath);
					return "success";
				}
				else
				{
					return "error";
				}
			}
			catch (Exception e)
			{
				typeLog("[Error] " + e.Message);
				return "error";
			}
		}

		// 从 Json 格式的交换机配置文件中解析数据
		public SwitchMapJson parseSwitchConfigFromJson(string configFilePath)
		{
			StreamReader reader = null;
			try
			{
				StringBuilder jsonDataBuilder = new StringBuilder();

				// 读取配置文件
				reader = new StreamReader(new FileStream(configFilePath, FileMode.Open));
				string currentLine = null;
				while (true)
				{
					currentLine = reader.ReadLine();
					if (currentLine == null)
					{
						break;
					}
					else
					{
						jsonDataBuilder.Append(currentLine);
					}
				}

				// 解析 Json
				SwitchMapJson switchMapJson = (SwitchMapJson)JsonConvert.DeserializeObject(jsonDataBuilder.ToString(), typeof(SwitchMapJson));

				// 检查 Mac 地址的正则表达式
				Regex r = new Regex(@"^([0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f])$");

				// 检查解析后的数据
				foreach (SwitchMapJsonUnit unit in switchMapJson.map)
				{
					// 检查 SubIndex
					int subIndex = -1;

					if (int.TryParse(unit.SubIndex, out subIndex) == false)
					{
						return null;
					}
					else if (subIndex < 1)
					{
						return null;
					}

					// 检查 Mac 地址
					if (r.IsMatch(unit.Mac.Trim().Replace("-", "").Replace(":", "")))
					{
						continue;
					}
					else
					{
						return null;
					}
				}

				return switchMapJson;

			}
			catch (Exception)
			{
				return null;
			}
			finally
			{
				if (reader != null)
				{
					reader.Close();
				}
			}
		}

		// 查询行映射表、分控映射表、附属设备映射表
		public List<MacTable> getLineMacTable(string type)
		{
            var m_type = type.Substring(0, 3);
            if (m_type.ToLower().Equals("row")) {
                return rowMacTable;
            } else if (m_type.ToLower().Equals("sub")) {
                return subMacTable;
            } else if (m_type.ToLower().Equals("equ")) {
                return equMacTable;
            }

			return null;
		}

		// 查询 id-mac 对应关系
		public List<string> getMacList()
		{
			return macList;
		}

		// IdMacPair 排序比较函数
		private static int IdMacPairSortCompare(IdMacPair obj1, IdMacPair obj2)
		{
			if (obj1 == null || obj2 == null)
			{
				return 0;
			}
			else
			{
				return obj1.startId - obj2.startId;
			}
		}

		#endregion

		#region 异形映射

		// 设置异形映射使能, 更新异形地址映射表
		public void setAddrMapEnable(bool enable)
		{
            addrMapEnable = enable;
			string logMessage = String.Format("[BaseCoreNet] SetAddrMapEnable, enable = " + enable);
			typeLog(logMessage);

            if (enable) {
                baseMapSrcType = MapSrcType.UseBin;
            } else {
                baseMapSrcType = MapSrcType.NoMap;
            }
            buildAddrMapTable(baseMapSrcType);
		}

        // 查询异形映射使能
        public bool getAddrMapEnable() {
            return addrMapEnable;
        }

		// 查询异形映射模式
		public int getBaseMapMode()
		{
			return (int)baseMapMode;
		}

		// 设置异形映射模式
		public void setBaseMapMode(MapMode mapMode)
		{
			baseMapMode = mapMode;
		}

		// 查询异形映射灯具排列
		public string getBaseColorString()
		{
			return baseColorString;
		}

		// 设置异形映射灯具排列
		public void setBaseColorString(string colorString)
		{
			baseColorString = colorString;
		}

		// 查询异形映射数据来源, 无异形/lwf/json
		public int getBaseMapSrcType()
		{
			return (int)baseMapSrcType;
		}

		// 查询异形地址映射表
		public int[] getAddrMapType()
		{
			return addrMapType;
		}

		// 查询异形地址映射表
		public int[] getAddrMap()
		{
			return addrMap;
		}

		// 查询异形地址映射表
		public int[] getAddrMapOffset()
		{
			return addrMapOffset;
		}

		// 从 lwf 格式的文件中读取数据并更新异形地址映射表, 同时启用异形, 使用 bin 数据源
		public int readLwfAndBuildMapTable(string lwfFilePath, bool withBuildAddrMapTable)
		{
			Console.ForegroundColor = ConsoleColor.DarkCyan;
			typeLog("[BaseCoreNet] readLwfAndBuildMapTable, lwfFilePath = " + lwfFilePath + ", withBuildAddrMapTable = " + withBuildAddrMapTable);
			Console.ResetColor();

			byte[] preBinData = binData;

			FileStream fs = null;
			BinaryReader reader = null;
			try
			{
				// 填充声明的设备链
				while (declareInfoIsBuilding == true)
				{
					Thread.Sleep(5);
				}
				declareInfoIsBuilding = true;
				declareMainControlList.Clear();
				DMainControl dMianControl = new DMainControl();
				declareMainControlList.Add(dMianControl);

				// 从 lwf 文件的 xml 区获取数据
				SystemTubeDataStore systemTubeDataStore = getXmlInfoFromLwf(lwfFilePath);

				// 设置灯具分辨率
				lwfSystemArea.X = systemTubeDataStore.systemTube.x;
				lwfSystemArea.Y = systemTubeDataStore.systemTube.y;
				typeLog("lwfSystemArea.X = " + lwfSystemArea.X + ", lwfSystemArea.Y = " + lwfSystemArea.Y);

				fs = new FileStream(lwfFilePath, FileMode.Open);
				reader = new BinaryReader(fs);

				// 读取文件头, 解析 bin 区的起始位置和长度
				byte[] header = reader.ReadBytes(0x005F);
				int binStart = header[0x30] << 24 | header[0x31] << 16 | header[0x32] << 8 | header[0x33];
				int binLength = header[0x36] << 24 | header[0x37] << 16 | header[0x38] << 8 | header[0x39];
				int binChannelStart = header[0x50] << 24 | header[0x51] << 16 | header[0x52] << 8 | header[0x53];
				int binChannelLength = header[0x56] << 24 | header[0x57] << 16 | header[0x58] << 8 | header[0x59];

				// 跳过 bin 区前的数据
				reader.ReadBytes(binStart - 0x005F);

				// 读取 bin-1 区数据
				binData = reader.ReadBytes(binLength);
				preBinData = null;

				// 读取 bin-2 区数据
				if (binChannelLength > 0)
				{
					reader.BaseStream.Position = binChannelStart;
					binDataChannel = reader.ReadBytes(binChannelLength);
				}
				else
				{
					binDataChannel = null;
				}

				// for debug
				/*
				BinaryWriter writer = new BinaryWriter(new FileStream("binData-pre.dat", FileMode.Create));
				writer.Write(binData);
				writer.Flush();
				writer.Close();
				*/

				subNumInBin1 = binData[0x102] << 8 | binData[0x0103];
				int maxValue = 0;
				for (int i = 0; i < subNumInBin1; i++)
				{
					int tmpMaxValue = binData[0x1000 + i * 16 + 2] << 8 | binData[0x1000 + i * 16 + 3];
					if (tmpMaxValue > maxValue)
					{
						maxValue = tmpMaxValue;
					}

					int totalLampNum = binData[0x1000 + i * 16 + 4] << 8 | binData[0x1000 + i * 16 + 5];

					// 填充声明的设备链
					DSubControl dSubControl = new DSubControl();
					dSubControl.mainControlIndex = 0;			// 目前只支持单主控
					dSubControl.subControlIndex = i;			// 分控索引
					dSubControl.declareLampNum = totalLampNum;	// 分控灯具数量
					dSubControl.declarePortNum = 8;				// 分控端口数量 - 固定为 8
					dMianControl.declareSubControlList.Add(dSubControl);

					// 将无效灯坐标设置为(0xffff,0xffff)
					SubData refSubData = systemTubeDataStore.subTube.subDataList[i];
					for (int portIndex = 0; portIndex < 8; portIndex++)
					{
						int maxLampNum = refSubData.portLampNumList[portIndex];
						for (int invalidLampIndex = maxLampNum; invalidLampIndex < 1024; invalidLampIndex++)
						{
							// 计算该分控在内部异形格式（bin）中的偏移
							int bin_offset = (i + 1) * 0x8000;

							// 计算该灯具在内部异形格式（bin）中的地址
							int bin_addr = bin_offset + invalidLampIndex * 32 + portIndex * 4;

							binData[bin_addr + 0] = 0xff;
							binData[bin_addr + 1] = 0xff;
							binData[bin_addr + 2] = 0xff;
							binData[bin_addr + 3] = 0xff;
						}
					}
				}
				maxLampNumInBin = maxValue;

				// for debug
				/*
				writer = new BinaryWriter(new FileStream("binData-post.dat", FileMode.Create));
				writer.Write(binData);
				writer.Flush();
				writer.Close();
				*/

				// 填充声明的设备链
				dMianControl.declareSubNum = subNumInBin1;

				// 读取端口映射数据（如果有）
				if (header[0x0013] == 0x01)
				{
					// 读取端口映射数据
					int portInfoStart = header[0x40] << 24 | header[0x41] << 16 | header[0x42] << 8 | header[0x43];
					int portInfoLength = header[0x46] << 24 | header[0x47] << 16 | header[0x48] << 8 | header[0x49];
					int portTotalNum = portInfoLength / 1024;
					byte[] portInfoData = new byte[1024];

					portColorModeList.Clear();
					for (int i = 0; i < portTotalNum; i++)
					{
						fs.Seek(portInfoStart + i * 1024, SeekOrigin.Begin);
						portInfoData = reader.ReadBytes(1024);

						if (portInfoData[0x01] == 0x00)		// 单一灯具
						{
							StringBuilder sb = new StringBuilder();
							List<int> colorList = new List<int>();

							byte channelNum = portInfoData[0x02];
							for (byte k = 0; k < channelNum; k++)
							{
								byte colorIndex = portInfoData[0x0010 + k * 2 + 1];
								if (colorIndex == 0)
								{
									sb.Append("R");
									colorList.Add(0);
								}
								else if (colorIndex == 1)
								{
									sb.Append("G");
									colorList.Add(1);
								}
								else if (colorIndex == 2)
								{
									sb.Append("B");
									colorList.Add(2);
								}
								else if (colorIndex == 3)
								{
									sb.Append("N");
									colorList.Add(3);
								}
								else if (colorIndex == 4)
								{
									sb.Append("W");
									colorList.Add(3);
								}
								else if (colorIndex == 5)
								{
									sb.Append("Y");
									colorList.Add(3);
								}
							}

							ColorMode colorMode = new ColorMode();
							colorMode.colorString = sb.ToString();
							colorMode.colorArray = colorList.ToArray();

							portColorModeList.Add(colorMode);
						}
					}
					lwfHasPortInfoData = true;
				}
				else
				{
					lwfHasPortInfoData = false;
					portColorModeList.Clear();
				}

				// 读取通道映射数据（如果有）
				if (header[0x0014] == 0x01)
				{
					lwfHasChannelInfoData = true;	// 此时通道映射数据已保存在 binDataChannel 中
				}
				else
				{
					lwfHasChannelInfoData = false;
				}

				if (withBuildAddrMapTable == true)
				{
					buildAddrMapTable(MapSrcType.UseBin);	// 启用异形映射
				}

				return binData.Length;
			}
			catch (Exception ex)
			{
				typeLog("[Error] " + ex.Message + ex.StackTrace);
				return -1;
			}
			finally
			{
				if (reader != null)
				{
					reader.Close();
				}
				if (fs != null)
				{
					fs.Close();
				}

				declareInfoIsBuilding = false;
			}
		}

		// 【停用】从 dxf 格式的文件中读取数据并更新异形地址映射表, 同时启用异形, 使用 bin 数据源
		public int readDxfAndBuildMapTable(string dxfFilePath, bool withBuildAddrMapTable)
		{
			typeLog("[BaseCoreNet] readLwfAndBuildMapTable\ndxfFilePath = " + dxfFilePath + ", withBuildAddrMapTable = " + withBuildAddrMapTable);

			int returnLength = -1;
			FileStream fs = null;
			BinaryReader reader = null;
			try
			{
				CDxfInOutClass dxfInOut = new CDxfInOutClass();

				// 从 dxf 文件中解析 bin 区数据, 结果保存在 \dxfParse.bin 中
				dxfInOut.GetDxfData(dxfFilePath);
				typeLog("Parse dxf data done!");

				// 填充声明的设备链
				while (declareInfoIsBuilding == true)
				{
					Thread.Sleep(5);
				}
				declareInfoIsBuilding = true;
				declareMainControlList.Clear();
				DMainControl dMianControl = new DMainControl();
				declareMainControlList.Add(dMianControl);

				lwfSystemArea = new Point(-1, -1);

				// 读取 bin 区数据
				fs = new FileStream(getAppPath() + @"\dxfParse.bin", FileMode.Open);
				reader = new BinaryReader(fs);
				binData = reader.ReadBytes((int)fs.Length);

				subNumInBin1 = binData[0x102] << 8 | binData[0x0103];
				int maxValue = 0;
				for (int i = 0; i < subNumInBin1; i++)
				{
					int tmpMaxValue = binData[0x1000 + i * 16 + 2] << 8 | binData[0x1000 + i * 16 + 3];
					if (tmpMaxValue > maxValue)
					{
						maxValue = tmpMaxValue;
					}

					int totalLampNum = binData[0x1000 + i * 16 + 4] << 8 | binData[0x1000 + i * 16 + 5];

					// 填充声明的设备链
					DSubControl dSubControl = new DSubControl();
					dSubControl.mainControlIndex = 0;			// 目前只支持单主控
					dSubControl.subControlIndex = i;			// 分控索引
					dSubControl.declareLampNum = totalLampNum;	// 分控灯具数量
					dSubControl.declarePortNum = 8;				// 分控端口数量 - 固定为 8
					dMianControl.declareSubControlList.Add(dSubControl);
				}
				maxLampNumInBin = maxValue;

				// 填充声明的设备链
				dMianControl.declareSubNum = subNumInBin1;

				// dxf 文件中没有端口映射数据
				lwfHasPortInfoData = false;
				portColorModeList.Clear();

				if (withBuildAddrMapTable)
				{
					buildAddrMapTable(MapSrcType.UseBin);	// 启用异形
				}

				returnLength = binData.Length;
			}
			catch (Exception)
			{
				typeLog("[Error] readDxfAndBuildMapTable Error!");
				returnLength = -1;
			}
			finally
			{
				if (reader != null)
				{
					reader.Close();
				}

				declareInfoIsBuilding = false;
			}

			try
			{
				if (File.Exists(getAppPath() + @"\dxfParse.bin"))
				{
					File.Delete(getAppPath() + @"\dxfParse.bin");
				}
			}
			catch (Exception)
			{
			}


			return returnLength;
		}

		// 【停用】从 json 格式的文件中读取数据并更新异形地址映射表, 同时启用异形, 使用 json 数据源
		public bool readJsonAndBuildMapTable(string jsonFilePath, bool withBuildAddrMapTable)
		{
			typeLog("[BaseCoreNet] readLwfAndBuildMapTable\njsonFilePath = " + jsonFilePath + ", withBuildAddrMapTable = " + withBuildAddrMapTable);

			StreamReader sr = null;
			try
			{
				FileInfo info = new FileInfo(jsonFilePath);
				if (!info.Exists)
				{
					typeLog("[Error] No Json File: " + jsonFilePath);
					return false;
				}

				// 从文件中读取数据
				sr = new StreamReader(new FileStream(jsonFilePath, FileMode.Open));
				StringBuilder sb = new StringBuilder();
				while (true)
				{
					string line = sr.ReadLine();

					if (line == null)
					{
						break;
					}
					else if (line.Trim().Equals(""))
					{
						continue;
					}

					sb.Append(line.Trim());
				}

				// 解析 json
				string jsonData = sb.ToString();
				addrMapJson = (AddrMapJson)JsonConvert.DeserializeObject(jsonData, typeof(AddrMapJson));

				// 将 addrMapJson 中的数据转化为内部数据结构, 存储在 subInfoList 中, 同时计算统计信息
				bool parseRes = addrMapJson.collectSubInfo();

				// json 解析正常
				if (parseRes == true)
				{
					if (withBuildAddrMapTable)
					{
						buildAddrMapTable(MapSrcType.UseJson);	// 启用异形
					}
				}
				else
				{
					typeLog("[Error] Json Parse Error!");
				}

				return true;
			}
			catch (Exception ex)
			{
				typeLog("[Error] " + ex.Message + ex.StackTrace);
				return false;
			}
			finally
			{
				if (sr != null)
				{
					sr.Close();
				}
			}
		}

		// 更改 baseMapMode 和 baseColorString
		public bool changeMapModeAndColorString(MapMode newMapMode, string newColorString)
		{
			if (newColorString == null)
			{
				return false;
			}

			// 检查 newColorString 合法性
			newColorString = newColorString.ToUpper();
			foreach (char c in newColorString)
			{
				if (c == 'R' || c == 'G' || c == 'B' || c == 'W' || c == 'N')
				{
					continue;
				}
				else
				{
					return false;
				}
			}

			// 设置 baseMapMode 和 baseColorString
			baseMapMode = newMapMode;
			baseColorString = newColorString;

			typeLog("[BaseCoreNet] changeMapModeAndColorString, baseMapMode = " + baseMapMode + ", baseColorString = " + baseColorString);

			// 重新生成映射表
			buildAddrMapTable(baseMapSrcType);

			return true;
		}

		// 获取声明的设备链信息
		public List<DMainControl> getDeclareMainControlList()
		{
			return cloneDeclareInfo();
		}

		// 清除从布线文件中读取的声明的设备链信息
		public void clearDeclareMainControlList()
		{
			declareMainControlList.Clear();
		}

		// 复制声明的设备链信息用于返回
		private List<DMainControl> cloneDeclareInfo()
		{
			try
			{
				while (declareInfoIsBuilding == true)
				{
					Thread.Sleep(5);
				}
				declareInfoIsBuilding = true;

				if (declareMainControlList == null)
				{
					return null;
				}

				List<DMainControl> returnList = new List<DMainControl>();
				foreach (DMainControl srcMainControl in declareMainControlList)
				{
					DMainControl dstMainControl = srcMainControl.Clone();
					returnList.Add(dstMainControl);
				}

				return returnList;
			}
			catch
			{
				return null;
			}
			finally
			{
				declareInfoIsBuilding = false;
			}
		}

		// 设置用户在界面上选择的颜色模式，0-三色，1-四色纯，2-四色增强，255-默认
		public void setUserColorType(int userColorTypeValue)
		{
			if (userColorTypeValue >= 0 && userColorTypeValue <= 255)
			{
				userColorType = userColorTypeValue;
                // 测试sub下发模式
                //if (userColorChangedHandle != null)
                //{
                //    userColorChangedHandle(userColorType);
                //}
                //通信包模式
                for (int k = 0; k < netDeviceList.Count; k++)
                {
                    NetDevice dev = (NetDevice)netDeviceList[k];
                    dev.packetSender.setUserColorType(userColorTypeValue);
                }
				string logMessage = String.Format("setUserColorType, typeValue = " + userColorType);
                
				typeLog(logMessage);

                buildAddrMapTable(); 
			}
		}

        public void setUserChannelLvl(int channelLvl)
        {
            this.userChannelLvl = channelLvl;
        }

		// 设置 DatPlayForm 播放掩码
		public void setAddrMapDatPlayingMask(bool maskValue)
		{
			string logMessage = String.Format("setAddrMapDatPlayingMask, maskValue = " + maskValue);
			typeLog(logMessage);

			if (maskValue == false && addrMapEnable == true)
			{
				buildAddrMapTable(MapSrcType.UseBin);	// 启用异形映射
			}

			if (maskValue == true)
			{
				setAddrMapEnable(false);
			}
 
			addrMapDatPlayingMask = maskValue;
		}

		#endregion

		#region L9 硬件设置

		// 下载布线 - 查询布线文件中的分控数量
		public int getSubControlNumInLwf()
		{
			if (binData == null)
			{
				return -1;
			}
			else
			{
				return subNumInBin1;
			}
		}

		// 下载布线 - 写入分控Flash
		public bool startDownloadAddrMapSubSend(SelectedSubInfo selectedSubInfo, bool withPreTreat, bool testMode)
		{
			if (getSubEquActiveState() == false)
			{
				return false;
			}
			else
			{
				Thread t = new Thread(() =>
				{
					// 硬件配置操作前处理
					configPreTreat();

					// 主逻辑动作
					int manalOffset = selectedSubInfo.enableManualOffset == false ? 0 : selectedSubInfo.manualOffset;
					if (selectedSubInfo.useAddrMapOffsetTable == false)
					{
						subControlDownloadAddrMap(selectedSubInfo, selectedSubInfo.overall, withPreTreat, testMode, null, manalOffset);
					}
					else
					{
						subControlDownloadAddrMap(selectedSubInfo, selectedSubInfo.overall, withPreTreat, testMode, selectedSubInfo.addrMapOffsetList, manalOffset);
					}

					// 硬件配置操作后处理
					configPostTreat();
				});
				t.IsBackground = true;
				t.Start();
				return true;
			}
		}

		// 清除布线 - 写入分控Flash
		public bool startClearAddrMapSubSend(SelectedSubInfo selectedSubInfo, bool overall, bool testMode)
		{
			if (getSubEquActiveState() == false)
			{
				return false;
			}
			else
			{
				Thread t = new Thread(() =>
				{
					// 硬件配置操作前处理
					configPreTreat();

					// 主逻辑动作
					subControlClearAddrMap(selectedSubInfo, overall, testMode);

					// 硬件配置操作后处理
					configPostTreat();
				});
				t.IsBackground = true;
				t.Start();
				return true;
			}
		}

		// 下载sub - 模式：应用
		public bool startApplySubSend(byte[] subData4Send, SelectedSubInfo selectedSubInfo, bool overall, bool withSplit1)
		{
			if (getSubEquActiveState() == false)
			{
				return false;
			}
			else
			{
				Thread t = new Thread(() =>
				{
					// 硬件配置操作前处理
					configPreTreat();

					// 主逻辑动作
					subControlDownloadSub(subData4Send, "apply", selectedSubInfo, overall, withSplit1);

					// 硬件配置操作后处理
					configPostTreat();
				});
				t.IsBackground = true;
				t.Start();
				return true;
			}
		}

		// 下载sub - 模式：测试
		public bool startTestSubSend(byte[] subData4Send, SelectedSubInfo selectedSubInfo, bool overall, bool withSplit1)
		{
			if (getSubEquActiveState() == false)
			{
				return false;
			}
			else
			{
				Thread t = new Thread(() =>
				{
					// 硬件配置操作前处理
					configPreTreat();

					// 主逻辑动作
					subControlDownloadSub(subData4Send, "test", selectedSubInfo, overall, withSplit1);

					// 硬件配置操作后处理
					configPostTreat();
				});
				t.IsBackground = true;
				t.Start();
				return true;
			}
		}

		// 下载布线 - 设置等待标志位 - 下载布线内部异常时使用
		public void setDownloadLwfWaitFlag(bool value)
		{
			downloadLwfWaitFlag = value;
		}

		// 下载布线 - 设置取消标志位 - 下载布线内部异常时使用
		public void setDownloadLwfBreakFlag(bool value)
		{
			downloadLwfBreakFlag = value;
		}

		// 下载布线 - 设置等待标志位 - 下载布线时使用
		public void setPauseDownloadAddrMap(bool value)
		{
			pauseDownloadAddrMap = value;
		}

		// 下载布线 - 设置取消标志位 - 下载布线时使用
		public void setForceStopDownloadAddrMap(bool value)
		{
			forceStopDownloadAddrMap = value;
		}

		// 下载布线 - 设置等待标志位 - 清除布线时使用
		public void setPauseClearAddrMap(bool value)
		{
			pauseClearAddrMap = value;
		}

		// 下载布线 - 设置取消标志位 - 清除布线时使用
		public void setForceStopClearAddrMap(bool value)
		{
			forceStopClearAddrMap = value;
		}

		// 下载布线 - 设置等待标志位 - 下载sub文件时使用
		public void setPauseDownloadSub(bool value)
		{
			pauseDownloadSub = value;
		}

		// 下载布线 - 设置取消标志位 - 下载sub文件时使用
		public void setForceStopDownloadSub(bool value)
		{
			forceStopDownloadSub = value;
		}

		#endregion

		#region 灯具巡检

		// 设置灯具巡检标记, 灯具巡检期间, 停止拓扑结构检测
		public void setLampCheckProcessing(bool processing)
		{
			lampCheckProcessing = processing;

			if (lampCheckProcessing == true)
			{
				// 后台检测使能清除
				topologyDetectEnable = false;
			}
			else
			{
				// 后台检测使能置位
                topologyDetectEnable = true;
            }
            setSubAutoDetectEnable_rdm(0, !processing, true);
		}

        public void setSubAutoDetectEnable_rdm(int subIndex, bool enable, bool overall) {
            var devIndex = getActiveDevIndex();
            if (devIndex < 0) {
                return;
            }
            var data = new byte[256];
            data[0] = (byte)(enable ? 1 : 0);
            writeSubMem(devIndex, subIndex, 0x20000800, data, overall);

            Console.WriteLine("Auto Detect RDM : {0}", enable);
        }

		#endregion

		#region 子控（解码器）

		// 设置解码器控制状态
		public void setControlState(int subIndex, bool recCntEnable, bool pixelDetectEnable)
		{
			// 网口下的操作 - 通过周期发送显示设置包实现
			DispConfigPacketPara configPara = new DispConfigPacketPara();
			configPara.overall = true;
			configPara.Led_Power = ParaState.OFF;
			configPara.Fidelity = ParaState.OFF;
			configPara.Disp_Lock = ParaState.OFF;
			configPara.Disp_Black = ParaState.OFF;
			configPara.Point_Correction = ParaState.OFF;
			configPara.Chroma_Adjust = ParaState.OFF;
			configPara.Error_Check = ParaState.OFF;
			configPara.Virtual_Screen = ParaState.REAL;

			configPara.Forced_Sync = ParaState.OFF;
			configPara.Local_Pos_Flag = ParaState.OFF;
			configPara.Is_Equ_Para = ParaState.OFF;

			if (recCntEnable == true)
			{
				configPara.Mcu_RW_Enable = ParaState.ON;
			}
			else
			{
				configPara.Mcu_RW_Enable = ParaState.OFF;
			}

			if (pixelDetectEnable == true)
			{
				configPara.MBI_6027_Enable = ParaState.ON;
			}
			else
			{
				configPara.MBI_6027_Enable = ParaState.OFF;
			}

			setDispConfigPacketPara(configPara);
		}

        // 设置解码器UID
        public void setLampUID(int subIndex, int subPortIndex, int miniIndex, byte[] uid)
        {
            var devIndex = getActiveDevIndex();

            if (devIndex < 0) {
                return;
            }

            var cmdData = new byte[256];

            cmdData[2] = 0x28;

            var index = 8;
            Tools.addByte(ref cmdData, uid, ref index);

            setMCUConfigEnable(subIndex, subPortIndex, miniIndex);

            sendMiniCmdWithCheck(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);
        }

        // 对解码器FLASH地址写入指定数据
        public void setLampFlashConfig(int subIndex, int subPortIndex, int miniIndex, uint flashAddr, byte[] data) {
            var devIndex = getActiveDevIndex();

            if (devIndex < 0) {
                return;
            }

            var cmdLength = 13 + data.Length + 1; // 命令包长度包括校验和
            var cmdData = new byte[256];

            cmdData[2] = 0x75;

            var index = 8;
            var addrData = Tools.uint2Byte4(flashAddr);
            Tools.addByte(ref cmdData, addrData, ref index);
            Tools.addByte(ref cmdData, (byte)data.Length, ref index);
            Tools.addByte(ref cmdData, data, ref index);

            setMCUConfigEnable(subIndex, subPortIndex, miniIndex);

            sendMiniCmdWithCheckEx(devIndex, subIndex, subPortIndex, miniIndex, cmdData, cmdLength);
        }

        // 设置解码器级联方式 0 - 串口级联，1 - IO级联
        public void setLampCascade(int subIndex, int subPortIndex, int miniIndex, byte type) {
            var data = new byte[] { type };
            setLampFlashConfig(subIndex, subPortIndex, miniIndex, (uint)0x3790, data);
        }

        // 设置解码器PWM管脚 0 - 1.2，1 - 1.1
        public void setLampPWMPin(int subIndex, int subPortIndex, int miniIndex, byte type) {
            var data = new byte[] { type };
            setLampFlashConfig(subIndex, subPortIndex, miniIndex, (uint)0x3791, data);
        }

		// 获取灯具状态参数
		public LampStatePara getLampStatePara(int subIndex, int subPortIndex, int miniIndex)
		{
			int devIndex = getActiveDevIndex();
			
			if (devIndex < 0)
			{
				return null;
			}

			LampStatePara returnPara = null;

			string[] res = getMiniVersionAndRdmSeq(subIndex, subPortIndex, miniIndex);
			if (res[0] == "")
			{
				return returnPara;
			}

			returnPara = new LampStatePara();
			returnPara.SubIndex = subIndex;								// 分控索引
			returnPara.PortIndex = subPortIndex;						// 端口索引
			returnPara.LampIndex = miniIndex;							// 灯具索引
			returnPara.Version = res[0];								// 版本号
			returnPara.RdmSeg = res[1];									// RDM编号
			
			byte[] cmdData = new byte[256];
			
			cmdData[2] = 0x61;

			int cmdCommNum = 0;
			try
			{
				cmdCommNum = sendMiniCmdWithBaseAddrAndCheck(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32, 0x20A00000);
			}
			catch (Exception)
			{
				return null;
			}

			Thread.Sleep(150);

			byte[] ackData = new byte[256];
            bool success = getMiniAck(devIndex, subIndex, subPortIndex, ackData, cmdCommNum);

			if (success == true)
			{
				returnPara.PacketId = ackData[3];							// 包序号
				returnPara.LampAddr = ackData[4] | ackData[5] << 8;			// 灯具地址
				returnPara.PacketLength = ackData[6] | ackData[7] << 8;		// 包长度
				returnPara.DmxAddr = ackData[8] | ackData[9] << 8;			// DMX地址
				returnPara.DmxId = ackData[10] | ackData[11] << 8;			// DMX编号
				returnPara.Current = ackData[12] | ackData[13] << 8;		// 电流参数
				returnPara.Voltage = ackData[14] | ackData[15] << 8;		// 电压参数
				returnPara.Temperature = ackData[16] | ackData[17] << 8;	// 温度参数
				returnPara.ErrorFlag = ackData[18];							// 错误状态标志
				returnPara.LampId = ackData[19] | ackData[20] << 8 | ackData[21] << 16 | ackData[22] << 24;	// 灯具ID（低位在前）

				returnPara.LampChannel = -1;		// 灯具通道数
				if (ackData[6] >= 24)
				{
					returnPara.LampChannel = ackData[23] | ackData[24] << 8;	// 灯具ID（低位在前）
				}

				returnPara.DebugInfo = "";		// 调试信息
				if (ackData[6] >= 34)
				{
					returnPara.DebugInfo = "";
					for (int i = 29; i <= 34; i++)
					{
						returnPara.DebugInfo = returnPara.DebugInfo + ackData[i].ToString("X2");
					}
				}
			}
			return returnPara;
		}

        public LampStatePara getAmpStatePara(int subIndex, int subPortIndex)
        {
            int devIndex = getActiveDevIndex();

            if (devIndex < 0)
            {
                return null;
            }

            LampStatePara returnPara = null;

            string[] res = getAmpVersion(subIndex, subPortIndex);
            if (res[0] == "")
            {
                return returnPara;
            }

            returnPara = new LampStatePara();
            returnPara.SubIndex = subIndex;								// 分控索引
            returnPara.PortIndex = subPortIndex;						// 端口索引
            returnPara.LampIndex = 0xffff;							// 灯具索引
            returnPara.Version = res[0];								// 版本号
            returnPara.RdmSeg = res[1];									// RDM编号

            byte[] cmdData = new byte[256];

            cmdData[2] = 0x61;

            int cmdCommNum = 0;
            try
            {
                cmdCommNum = sendAmpCmd(devIndex, subIndex, subPortIndex, cmdData, 32);
            }
            catch (Exception)
            {
                return null;
            }

            Thread.Sleep(50);

            byte[] ackData = new byte[256];
            bool success = getMiniAck(devIndex, subIndex, subPortIndex, ackData, cmdCommNum);

            if (success == true)
            {
                returnPara.PacketId = ackData[3];							// 包序号
                returnPara.LampAddr = ackData[4] | ackData[5] << 8;			// 灯具地址
                returnPara.PacketLength = ackData[6] | ackData[7] << 8;		// 包长度
                returnPara.DmxAddr = ackData[8] | ackData[9] << 8;			// DMX地址
                returnPara.DmxId = ackData[10] | ackData[11] << 8;			// DMX编号
                returnPara.Current = ackData[12] | ackData[13] << 8;		// 电流参数
                returnPara.Voltage = ackData[14] | ackData[15] << 8;		// 电压参数
                returnPara.Temperature = ackData[16] | ackData[17] << 8;	// 温度参数
                returnPara.ErrorFlag = ackData[18];							// 错误状态标志
                returnPara.LampId = ackData[19] | ackData[20] << 8 | ackData[21] << 16 | ackData[22] << 24;	// 灯具ID（低位在前）

                returnPara.LampChannel = -1;		// 灯具通道数
                if (ackData[6] >= 24)
                {
                    returnPara.LampChannel = ackData[23] | ackData[24] << 8;	// 灯具ID（低位在前）
                }

                returnPara.DebugInfo = "";		// 调试信息
                if (ackData[6] >= 34)
                {
                    returnPara.DebugInfo = "";
                    for (int i = 29; i <= 34; i++)
                    {
                        returnPara.DebugInfo = returnPara.DebugInfo + ackData[i].ToString("X2");
                    }
                }
            }

            return returnPara;
        }

        public LampStatePara setLampStatePara(int subIndex, int subPortIndex, int miniIndex)
        {
            int devIndex = getActiveDevIndex();

            if (devIndex < 0)
            {
                return null;
            }

            LampStatePara returnPara = null;

            string[] res = getMiniVersionAndRdmSeq(subIndex, subPortIndex, miniIndex);
            if (res[0] == "")
            {
                return returnPara;
            }

            returnPara = new LampStatePara();
            returnPara.SubIndex = subIndex;								// 分控索引
            returnPara.PortIndex = subPortIndex;						// 端口索引
            returnPara.LampIndex = miniIndex;							// 灯具索引
            returnPara.Version = res[0];								// 版本号
            returnPara.RdmSeg = res[1];									// RDM编号

            byte[] cmdData = new byte[256];

            cmdData[2] = 0x61;

            int cmdCommNum = 0;
            try
            {
                cmdCommNum = sendMiniCmdWithBaseAddr(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32, 0x20A00000);
            }
            catch (Exception)
            {
                return null;
            }

            Thread.Sleep(50);

            byte[] ackData = new byte[256];
            bool success = getMiniAck(devIndex, subIndex, subPortIndex, ackData, cmdCommNum);

            if (success == true)
            {
                returnPara.PacketId = ackData[3];							// 包序号
                returnPara.LampAddr = ackData[4] | ackData[5] << 8;			// 灯具地址
                returnPara.PacketLength = ackData[6] | ackData[7] << 8;		// 包长度
                returnPara.DmxAddr = ackData[8] | ackData[9] << 8;			// DMX地址
                returnPara.DmxId = ackData[10] | ackData[11] << 8;			// DMX编号
                returnPara.Current = ackData[12] | ackData[13] << 8;		// 电流参数
                returnPara.Voltage = ackData[14] | ackData[15] << 8;		// 电压参数
                returnPara.Temperature = ackData[16] | ackData[17] << 8;	// 温度参数
                returnPara.ErrorFlag = ackData[18];							// 错误状态标志
                returnPara.LampId = ackData[19] | ackData[20] << 8 | ackData[21] << 16 | ackData[22] << 24;	// 灯具ID（低位在前）

                returnPara.LampChannel = -1;		// 灯具通道数
                if (ackData[6] >= 24)
                {
                    returnPara.LampChannel = ackData[23] | ackData[24] << 8;	// 灯具ID（低位在前）
                }

                returnPara.DebugInfo = "";		// 调试信息
                if (ackData[6] >= 34)
                {
                    returnPara.DebugInfo = "";
                    for (int i = 29; i <= 34; i++)
                    {
                        returnPara.DebugInfo = returnPara.DebugInfo + ackData[i].ToString("X2");
                    }
                }
            }

            return returnPara;
        }

		// 打开自动级联地址
		public void setMcuOpenAddr(int subIndex, int subPortIndex, int miniIndex, byte para)
		{
			int devIndex = getActiveDevIndex();
			
			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];
			
			cmdData[2] = 0x2b;
			cmdData[8] = para;

			sendMiniCmd(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);
		}

		// 保存 DMX 起始通道地址
		public void setMcuSaveDMXAddr(int subIndex, int subPortIndex, int miniIndex, int addr, int para)
		{
            setMCUConfigEnable(subIndex, subPortIndex, miniIndex);
			int devIndex = getActiveDevIndex();
			
			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];
			
			cmdData[2] = 0x29;
			cmdData[8] = (byte)para;
			cmdData[9] = (byte)(addr & 0xff);			// 地址低
			cmdData[10] = (byte)((addr >> 8) & 0xff);	// 地址高

            sendMiniCmdWithCheck(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);
		}

		// 清除 DMX 起始通道地址, 解码器地址, NUM
		public void setMcuDelMode(int subIndex, int subPortIndex, int miniIndex, byte addr)
		{
			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];
			
			cmdData[2] = addr;
            if (addr == 0x2e)
            {
                setMCUConfigEnable(subIndex, subPortIndex, miniIndex);

                sendMiniCmdWithCheck(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);
            }
            else
            {
                sendMiniCmd(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);
            }
		}

		// 分控 DMX 写址
        public void writeSubDmxStartAddr(int subIndex, int channelNum, int startAddr, int dmx_w_type, bool usePin4, byte[] config1, byte[] config2, uint portmask = 0)
		{
			int i, dmxAddr;
			byte[] dmxAddrBin = new byte[1024];
			byte[] memData = new byte[256];
			uint memAddr;
			int chip_mode, clk_div, chip_max, sm1651x_config, sm1651x_clk_div;
			byte port_mask;

            int effecttype = (dmx_w_type >> 8) & 0xff;
            dmx_w_type = dmx_w_type & 0xff;

			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

            // 芯片模式：0 - TM512/UCS512A/ZD512, 1 - UCS512C, 2 - SM16512, 3 -  UCS512D, 4 - SM17500, 5 - UCS512E
			chip_mode = dmx_w_type;

			// 计算每个芯片地址
			for (i = 0; i < 512; i++)
			{
				dmxAddr = startAddr + i * channelNum;
                if (chip_mode < 2 || chip_mode == 5)  // UCS512A/B/C/E 和 TM512，1通道写入数据为0
				{
					dmxAddr = dmxAddr - 1;
				}
				if (dmxAddr < 0)   // 地址保护
				{
					dmxAddr = 0;
				}
                if (chip_mode == 2)
                {
                    dmxAddr = dmxAddr | (effecttype & 0x07) << 12;
                }
				dmxAddrBin[i * 2] = (byte)(dmxAddr & 0xff);
				dmxAddrBin[i * 2 + 1] = (byte)((dmxAddr >> 8) & 0xff);
			}

            if (chip_mode < 256) { //进行参数及电流配置时不写址
                for (i = 0; i < 256; i++) {
                    memData[i] = dmxAddrBin[i];
                }
                memAddr = 0x2800;
                writeSubMem(devIndex, subIndex, memAddr, memData, false);
                Thread.Sleep(10);
                for (i = 0; i < 256; i++) {
                    memData[i] = dmxAddrBin[i + 256];
                }
                memAddr = 0x2900;
                writeSubMem(devIndex, subIndex, memAddr, memData, false);
                Thread.Sleep(10);
                for (i = 0; i < 256; i++) {
                    memData[i] = dmxAddrBin[i + 512];
                }
                memAddr = 0x2A00;
                writeSubMem(devIndex, subIndex, memAddr, memData, false);
                Thread.Sleep(10);
                for (i = 0; i < 256; i++) {
                    memData[i] = dmxAddrBin[i + 768];
                }
                memAddr = 0x2B00;
                writeSubMem(devIndex, subIndex, memAddr, memData, false);
                Thread.Sleep(10);
            }
			int dmxAddrGap = channelNum;		// DMX地址间隔 = 界面上的地址通道数
			int dmxStartAddr = startAddr - 1;	// DMX起始地址 = 界面上DMX起始地址-1
			byte[] dmxStartAddrData = Tools.int2Byte2(dmxStartAddr);

			if (chip_mode == 2)
			{
				chip_max = 512 - 1;
				clk_div = (4 * 125) - 2;
                //int sm1651x_reset = effecttype;			// 上电状态：0-不亮, 1-50%白, 2-100%白, 3-50%蓝
                //int sm1651x_close4 = channelNum == 4 ? 0 : 1;			// 1-关闭第四通道，0-不关闭第四通道
                sm1651x_config = effecttype;
				sm1651x_clk_div = (147 * 125 - 2);
			}
			else if (chip_mode == 1)
			{
				chip_max = 512 - 1;
				clk_div = (4 * 125) - 2;
				sm1651x_config = 0;
				sm1651x_clk_div = 0;
			}
			else
			{
				chip_max = 512 - 1;
				clk_div = (50 * 125) - 2;
				sm1651x_config = 0;
				sm1651x_clk_div = 0;
			}

			// 通道使能, bit0~bit7对应端口1~8, 0-使能, 1-禁止
			port_mask = (byte)portmask;

			for (i = 0; i < 256; i++)
			{
				memData[i] = 0;
			}

			memData[0] = 0x01;										// bit[0]=1, 写址使能
			memData[1] = (byte)((dmxAddrGap & 0xf0) >> 4);			// bit[3:0]:DMX地址间隔高4bit
			memData[2] = (byte)dmxStartAddrData[0];					// bit[7:0]:DMX起始地址低8bit
			memData[3] = (byte)(((dmxAddrGap & 0x0f) << 4) + (dmxStartAddrData[1] & 0x0F));		// bit[7:4]:DMX地址间隔低4bit, bit[3:0]:DMX起始地址高4bit
            memData[0x0f] = (byte)((chip_mode >> 8) & 0xff);	    // 写址类型 高8bit
			memData[0x10] = (byte)(chip_mode & 0xff);				// 写址类型 低8bit
			memData[0x11] = (byte)(sm1651x_config & 0xff);			// 写址类型
			memData[0x12] = (byte)(clk_div & 0xff);					// bit分频-低
			memData[0x13] = (byte)((clk_div >> 8) & 0xff);			// bit分频-高
			memData[0x14] = (byte)(chip_max & 0xff);				// 芯片个数-低
			memData[0x15] = (byte)((chip_max >> 8) & 0xff);			// 芯片个数-高
            memData[0x16] = (byte)(port_mask & 0xff);				// 端口屏蔽[7:0]
            memData[0x1a] = (byte)((port_mask >> 8) & 0xff);		// 端口屏蔽[15:8]
            memData[0x1b] = (byte)((port_mask >> 16) & 0xff);		// 端口屏蔽[23:16]
            memData[0x1c] = (byte)((port_mask >> 24) & 0xff);		// 端口屏蔽[31:24]
			if (usePin4 == true)
			{
				memData[0x17] = 0x3c;
			}
			else
			{
				memData[0x17] = 0x00;
			}
			memData[0x18] = (byte)(sm1651x_clk_div & 0xff);			// 芯片个数-低
			memData[0x19] = (byte)((sm1651x_clk_div >> 8) & 0xff);	// 芯片个数-高
			memData[0x20] = 0x01;									// 写址使能

            if (config1 != null) {
                var configLen = Math.Min(32, config1.Length);
                Array.Copy(config1, 0, memData, 0x30, configLen);
            }
            if (config2 != null) {
                var currentLen = Math.Min(32, config2.Length);
                Array.Copy(config2, 0, memData, 0x50, currentLen);
            }
			memAddr = 0x2000;
			writeSubMem(devIndex, subIndex, memAddr, memData, false);
		}

		// 分控高级 DMX 写址
		public void writeSubDmxStartAddrPro(int subIndex, int portIndex, int[] dmx_addr, int startAddr, int dmx_w_type, bool usePin4)
		{
			int i;
			byte[] dmx_addr_bin = new byte[1024];
			byte[] memData = new byte[256];
			uint memAddr;
			int chip_mode, clk_div, chip_max, sm1651x_config, sm1651x_clk_div;
			byte port_mask;

            int effecttype = (dmx_w_type >> 8) & 0xff;
            dmx_w_type = dmx_w_type & 0xff;

			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			// 芯片模式：0 - TM512/UCS512A/ZD512, 1 - UCS512C, 2 - SM16512
			chip_mode = dmx_w_type;

			// 计算每个芯片地址
			for (i = 0; i < 512; i++)
			{
				int addr = dmx_addr[i];
				if (chip_mode < 2)  // UCS512A/B/C 和 TM512，1通道写入数据为0
				{
					addr = dmx_addr[i] - 1;
				}
				if (addr < 0)		// 地址保护
				{
					addr = 0;
				}
                if (chip_mode == 2)
                {
                    addr = addr | (effecttype & 0x07) << 12;
                }
				dmx_addr_bin[i * 2] = (byte)(addr & 0xff);
				dmx_addr_bin[i * 2 + 1] = (byte)((addr >> 8) & 0xff);
			}

			for (i = 0; i < 256; i++)
			{
				memData[i] = dmx_addr_bin[i];
			}
			memAddr = 0x2800;
			writeSubMem(devIndex, subIndex, memAddr, memData, false);

			for (i = 0; i < 256; i++)
			{
				memData[i] = dmx_addr_bin[i + 256];
			}
			memAddr = 0x2900;
			writeSubMem(devIndex, subIndex, memAddr, memData, false);

			for (i = 0; i < 256; i++)
			{
				memData[i] = dmx_addr_bin[i + 512];
			}
			memAddr = 0x2A00;
			writeSubMem(devIndex, subIndex, memAddr, memData, false);

			for (i = 0; i < 256; i++)
			{
				memData[i] = dmx_addr_bin[i + 768];
			}
			memAddr = 0x2B00;
			writeSubMem(devIndex, subIndex, memAddr, memData, false);

			int dmxAddrGap = 3;						// DMX地址间隔 = 3
			int dmxStartAddr = startAddr - 1;		// DMX起始地址 = 界面上DMX起始地址-1
			byte[] dmxStartAddrData = Tools.int2Byte2(dmxStartAddr);

			if (chip_mode == 2)
			{
				chip_max = 512 - 1;
				clk_div = (4 * 125) - 2;
                //int sm1651x_reset = effecttype;			// 上电状态：0-不亮, 1-50%白, 2-100%白, 3-50%蓝
                //int sm1651x_close4 = channelNum == 4 ? 0 : 1;			// 1-关闭第四通道，0-不关闭第四通道
                sm1651x_config = effecttype;
				sm1651x_clk_div = (147 * 125 - 2);
			}
			else if (chip_mode == 1)
			{
				chip_max = 512 - 1;
				clk_div = (4 * 125) - 2;
				sm1651x_config = 0;
				sm1651x_clk_div = 0;
			}
			else
			{
				chip_max = 512 - 1;
				clk_div = (50 * 125) - 2;
				sm1651x_config = 0;
				sm1651x_clk_div = 0;
			}

			// 通道使能, bit0~bit7对应端口1~8, 0-使能, 1-禁止
			byte tmp = (byte)(1 << portIndex);
			port_mask = (byte)(0xff & (~tmp));
			for (i = 0; i < 256; i++)
			{
				memData[i] = 0;
			}
			memData[0] = 0x01;										// bit[0]=1, 写址使能
			memData[1] = (byte)((dmxAddrGap & 0xf0) >> 4);			// bit[3:0]:DMX地址间隔高4bit
			memData[2] = (byte)dmxStartAddrData[0];					// bit[7:0]:DMX起始地址低8bit
			memData[3] = (byte)(((dmxAddrGap & 0x0f) << 4) + (dmxStartAddrData[1] & 0x0f));		// bit[7:4]:DMX地址间隔低4bit, bit[3:0]:DMX起始地址高4bit
			memData[0x10] = (byte)(chip_mode & 0xff);				// 写址类型
			memData[0x11] = (byte)(sm1651x_config & 0xff);			// 写址类型
			memData[0x12] = (byte)(clk_div & 0xff);					// bit分频-低
			memData[0x13] = (byte)((clk_div >> 8) & 0xff);			// bit分频-高
			memData[0x14] = (byte)(chip_max & 0xff);				// 芯片个数-低
			memData[0x15] = (byte)((chip_max >> 8) & 0xff);			// 芯片个数-高
			memData[0x16] = port_mask;								// 端口屏蔽
			if (usePin4 == true)
			{
				memData[0x17] = 0x3c;
			}
			else
			{
				memData[0x17] = 0x00;
			}
			memData[0x18] = (byte)(sm1651x_clk_div & 0xff);			// 芯片个数-低
			memData[0x19] = (byte)((sm1651x_clk_div >> 8) & 0xff);	// 芯片个数-高
			memData[0x20] = 0x01;									// 写址使能

			memAddr = 0x2000;
			writeSubMem(devIndex, subIndex, memAddr, memData, false);
		}

		// 向子控发送命令 - 返回发包序号（带有基地址参数）
		public void sendRdmCmdWithBaseAddr(int subIndex, int subPortIndex, byte[] cmdData, uint baseAddr)
		{
			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			sendDmxCmdWithBaseAddr(devIndex, subIndex, subPortIndex, cmdData, baseAddr);
		}

		// 读取子控反馈包 - 返回读操作成功标记（带有基地址参数）
		public bool getRdmAckWithBaseAddr(int subIndex, int subPortIndex, byte[] ackData, uint baseAddr)
		{
			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return true;
			}

			bool timeout = readDmxAckWithBaseAddr(devIndex, subIndex, subPortIndex, ackData, baseAddr);

			return timeout;
		}

		// 编程模式：跳转到 Reset
		public void setMcuReset(int subIndex, int subPortIndex, int miniIndex)
        {
            setMCUConfigEnable(subIndex, subPortIndex, miniIndex);

			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x01;

			sendMiniCmdWithCheck(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);
		}

		// 编程模式：跳转到 Init
		public void setMcuInit(int subIndex, int subPortIndex, int miniIndex)
        {
            setMCUConfigEnable(subIndex, subPortIndex, miniIndex);

			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x04;

            sendMiniCmdWithCheck(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);
		}

		// 编程模式：跳转到 Active
		public void setMcuActive(int subIndex, int subPortIndex, int miniIndex)
        {
            setMCUConfigEnable(subIndex, subPortIndex, miniIndex);

			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x05;

            sendMiniCmdWithCheck(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);
		}

        // AMP模式：跳转到 Reset
        public void setAmpReset(int subIndex, int subPortIndex)
        {
            int devIndex = getActiveDevIndex();

            if (devIndex < 0)
            {
                return;
            }

            byte[] cmdData = new byte[256];

            cmdData[2] = 0x81;

            sendAmpCmd(devIndex, subIndex, subPortIndex, cmdData, 36);
        }

        // AMP模式：跳转到 Init
        public void setAmpInit(int subIndex, int subPortIndex)
        {
            int devIndex = getActiveDevIndex();

            if (devIndex < 0)
            {
                return;
            }

            byte[] cmdData = new byte[256];

            cmdData[2] = 0x84;

            sendAmpCmd(devIndex, subIndex, subPortIndex, cmdData, 36);
        }

        // AMP模式：跳转到 Active
        public void setAmpActive(int subIndex, int subPortIndex)
        {
            int devIndex = getActiveDevIndex();

            if (devIndex < 0)
            {
                return;
            }

            byte[] cmdData = new byte[256];

            cmdData[2] = 0x85;

            sendAmpCmd(devIndex, subIndex, subPortIndex, cmdData, 36);
        }

        public void setAmpReceive(int subIndex, int subPortIndex, int addr, byte[] dataBuff)
        {
            int devIndex = getActiveDevIndex();

            if (devIndex < 0)
            {
                return;
            }

            byte[] cmdData = new byte[256];

            cmdData[2] = 0x83;
            cmdData[9] = (byte)(addr & 0xff);
            cmdData[10] = (byte)((addr >> 8) & 0xff);
            
            for (int i = 0; i < 128; i++)
            {
                cmdData[11 + i] = dataBuff[i];
            }

            sendAmpCmd(devIndex, subIndex, subPortIndex, cmdData, 143);
        }

        public string[] getAmpVersion(int subIndex, int subPortIndex)
        {
            int devIndex = getActiveDevIndex();

            if (devIndex < 0)
            {
                return new string[] { "", "" };
            }

            byte[] cmdData = new byte[256];

            cmdData[2] = 0x82;		// 命令

            int cmdCommNum = sendAmpCmd(devIndex, 1, 1, cmdData, 36, 0x0100);
            byte[] ackData = new byte[256];
            bool success = getAmpAck(devIndex, 1, 1, ackData, cmdCommNum);

            string receiveVersion = "";
            string rdmSeq = "";

            if (success == true)
            {
                for (int i = 9; i <= 24; i++)
                {
                    receiveVersion = receiveVersion + (char)ackData[i];
                }

                for (int i = 25; i <= 30; i++)
                {
                    rdmSeq = rdmSeq + ackData[i].ToString("X2");
                }
            }

            return new string[] { receiveVersion, rdmSeq };
        }
        // 设置时间锁
        public bool setTimeLock(int subIndex, int subPortIndex, int miniIndex, int time, byte[] password)
        {
            int devIndex = getActiveDevIndex();

            if (devIndex < 0)
            {
                return false;
            }

            byte[] cmdData = new byte[256];

            cmdData[2] = 0x75;		// 命令

            cmdData[8] = (byte)(time & 0xff);
            cmdData[9] = (byte)((time >> 8) & 0xff);
            cmdData[10] = (byte)((time >> 16) & 0xff);
            cmdData[11] = (byte)((time >> 24) & 0xff);
            for (int i = 0; i < 6; i++)
            {
                cmdData[12 + i] = password[i];
            }

            int cmdCommNum = sendMiniCmd(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);

            byte[] ackData = new byte[256];
            bool success = getMiniAck(devIndex, subIndex, subPortIndex, ackData, cmdCommNum);

            bool result = false;
            if (success == true)
            {
                if (ackData[8] != 1)
                {
                    result = false;
                }
                else 
                {
                    result = true;
                }
            }

            return result;
        }

        // 设置时间锁
        public void setTimeLockLite(int subIndex, int subPortIndex, int miniIndex, int time, byte[] password)
        {
            int devIndex = getActiveDevIndex();

            if (devIndex < 0)
            {
                return;
            }

            byte[] cmdData = new byte[256];

            cmdData[2] = 0x75;		// 命令

            cmdData[8] = (byte)(time & 0xff);
            cmdData[9] = (byte)((time >> 8) & 0xff);
            cmdData[10] = (byte)((time >> 16) & 0xff);
            cmdData[11] = (byte)((time >> 24) & 0xff);
            for (int i = 0; i < 6; i++)
            {
                cmdData[12 + i] = password[i];
            }

            int cmdCommNum = sendMiniCmd(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);
        }

        // 解除时间锁
        public bool removeTimeLock(int subIndex, int subPortIndex, int miniIndex, byte[] password)
        {
            int devIndex = getActiveDevIndex();

            if (devIndex < 0)
            {
                return false;
            }

            byte[] cmdData = new byte[256];

            cmdData[2] = 0x76;		// 命令

            for (int i = 0; i < 6; i++)
            {
                cmdData[8 + i] = password[i];
            }

            int cmdCommNum = sendMiniCmd(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);

            byte[] ackData = new byte[256];
            bool success = getMiniAck(devIndex, subIndex, subPortIndex, ackData, cmdCommNum);

            bool result = false;
            if (success == true)
            {
                if (ackData[8] != 1)
                {
                    result = false;
                }
                else
                {
                    result = true;
                }
            }

            return result;
        }

        public void removeTimeLockLite(int subIndex, int subPortIndex, int miniIndex, byte[] password)
        {
            int devIndex = getActiveDevIndex();

            if (devIndex < 0)
            {
                return;
            }

            byte[] cmdData = new byte[256];

            cmdData[2] = 0x76;		// 命令

            for (int i = 0; i < 6; i++)
            {
                cmdData[8 + i] = password[i];
            }

            int cmdCommNum = sendMiniCmd(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);
        }

        public void setMcuConfig(int subIndex, int subPortIndex, int miniIndex, int addr, byte[] dataBuff, int length = 32)
        {
            setMCUConfigEnable(subIndex, subPortIndex, miniIndex);

            int devIndex = getActiveDevIndex();

            if (devIndex < 0)
            {
                return;
            }

            byte[] cmdData = new byte[256];

            cmdData[2] = 0x4C;
            cmdData[8] = (byte)(addr & 0xff);
            cmdData[9] = 0x37;
            cmdData[10] = (byte)(length & 0xff);
            cmdData[11] = (byte)((length >> 8) & 0xff);
            
            for (int i = 0; i < length; i++)
            {
                cmdData[12 + i] = dataBuff[i];
            }

            sendMiniCmdWithCheck(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 50);
        }

		public void setMcuReceive(int subIndex, int subPortIndex, int miniIndex, int addr, byte[] dataBuff)
		{
			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x10;
			cmdData[8] = (byte)(addr & 0xff);
			cmdData[9] = (byte)((addr >> 8) & 0xff);

			for (int i = 0; i < 128; i++)
			{
				cmdData[10 + i] = dataBuff[i];
			}

			sendMiniCmd(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 142);
		}

		public void setMcuWrite(int subIndex, int subPortIndex, int miniIndex, int addr)
		{
			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x11;

			cmdData[8] = (byte)(addr & 0xff);
			cmdData[9] = (byte)((addr >> 8) & 0xff);
			cmdData[10] = (byte)((addr >> 16) & 0xff);
			cmdData[11] = (byte)((addr >> 24) & 0xff);
			cmdData[12] = (byte)(1024 & 0xff);
			cmdData[13] = (byte)((1024 >> 8) & 0xff);

			sendMiniCmd(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);
		}

		public void setMcu8Write(int subIndex, int subPortIndex, int miniIndex, int addr, byte[] dataBuff)
		{
			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x12;
			cmdData[8] = (byte)(addr & 0xff);
			cmdData[9] = (byte)((addr >> 8) & 0xff);

			for (int i = 0; i < 64; i++)
			{
				cmdData[10 + i] = dataBuff[i];
			}

			sendMiniCmd(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 78);
		}

		public void setN76E003Write(int subIndex, int subPortIndex, int miniIndex, int addr, byte[] dataBuff)
		{
			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x13;
			cmdData[8] = (byte)(addr & 0xff);
			cmdData[9] = (byte)((addr >> 8) & 0xff);

			for (int i = 0; i < 128; i++)
			{
				cmdData[10 + i] = dataBuff[i];
			}

			sendMiniCmd(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 142);
		}

		public void setMcuStartCRC(int subIndex, int subPortIndex, int miniIndex, int addr, int dataLength)
		{
			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x17;
			
			cmdData[8] = (byte)(addr & 0xff);
			cmdData[9] = (byte)((addr >> 8) & 0xff);
			cmdData[10] = (byte)((addr >> 16) & 0xff);
			cmdData[11] = (byte)((addr >> 24) & 0xff);
			cmdData[12] = (byte)(dataLength & 0xff);
			cmdData[13] = (byte)((dataLength >> 8) & 0xff);
			cmdData[14] = 0x00;	// 区分 STM8 和 STM32

			sendMiniCmd(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);
		}

		public void setMcuWriteCRC(int subIndex, int subPortIndex, int miniIndex, uint crcValue, uint addr, uint dataLength)
		{
			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x18;
			
			cmdData[8] = (byte)(crcValue & 0xff);
			cmdData[9] = (byte)((crcValue >> 8) & 0xff);
			cmdData[10] = (byte)((crcValue >> 16) & 0xff);
			cmdData[11] = (byte)((crcValue >> 24) & 0xff);

			cmdData[12] = (byte)(addr & 0xff);
			cmdData[13] = (byte)((addr >> 8) & 0xff);
			cmdData[14] = (byte)((addr >> 16) & 0xff);
			cmdData[15] = (byte)((addr >> 24) & 0xff);
			cmdData[16] = (byte)(dataLength & 0xff);
			cmdData[17] = (byte)((dataLength >> 8) & 0xff);

			sendMiniCmd(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);
		}

		// 获取子控版本和状态
		public string[] getMiniVersionAndRdmSeq(int subIndex, int subPortIndex, int miniIndex)
        {
            setMCUConfigEnable(subIndex, subPortIndex, miniIndex);

			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return new string[] { "", "" };
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x14;		// 命令

            int cmdCommNum = sendMiniCmdWithCheck(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);

            Thread.Sleep(10);

			byte[] ackData = new byte[256];
			bool success = getMiniAck(devIndex, subIndex, subPortIndex, ackData, cmdCommNum);

			string receiveVersion = "";
			string rdmSeq = "";
			
			if (success == true)
			{
				for (int i = 8; i <= 19; i++)
				{
					receiveVersion = receiveVersion + (char)ackData[i];
				}

				for (int i = 24; i <= 29; i++)
				{
					rdmSeq = rdmSeq + ackData[i].ToString("X2");
				}
			}

			return new string[] { receiveVersion, rdmSeq };
		}

		// 设置灯具颜色模式
		public void setMcuColor(int subIndex, int subPort, int miniIndex, int colorMode)
		{
            setMCUConfigEnable(subIndex, subPort, miniIndex);
			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x44;
			cmdData[8] = (byte)colorMode;
            cmdData[9] = (byte)((colorMode >> 8) & 0xff);

            sendMiniCmdWithCheck(devIndex, subIndex, subPort, miniIndex, cmdData, 32);
		}

		// 设置灯具颜色模式
		public void setMcuChannle_old(int subIndex, int subPort, int miniIndex, byte channleNum)
		{
			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x68;
			cmdData[8] = channleNum;
			cmdData[9] = 0x00;

			sendMiniCmd(devIndex, subIndex, subPort, miniIndex, cmdData, 32);
		}

		// 设置亮度、速度、内控模式（常量、渐变、跳变、无自检）、无信号选择位、分色亮度值
		public void setMcuInnorControlMode(int subIndex, int subPort, int miniIndex, int lightValue, int speedValue, int innerMode, int noSignalMode, int[] rgbw)
		{
            setMCUConfigEnable(subIndex, subPort, miniIndex);
			
			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x31;
			cmdData[8] = (byte)lightValue;
			cmdData[9] = (byte)(lightValue >> 8);
			cmdData[10] = (byte)speedValue;
			cmdData[11] = (byte)innerMode;
			cmdData[12] = (byte)noSignalMode;
			cmdData[13] = (byte)(rgbw[0]);
			cmdData[14] = (byte)(rgbw[1]);
			cmdData[15] = (byte)(rgbw[2]);
			cmdData[16] = (byte)(rgbw[3]);

            sendMiniCmdWithCheck(devIndex, subIndex, subPort, miniIndex, cmdData, 32);
		}

		// 设置 PWM 曲线参数
		public void setMcuPWM(int subIndex, int subPort, int miniIndex, int pmax, int gamma, int add, int p_level, int start, int e_level)
        {
            setMCUConfigEnable(subIndex, subPort, miniIndex);
			
			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x42;
			cmdData[8] = (byte)pmax;
			cmdData[9] = (byte)(pmax >> 8);
			cmdData[10] = (byte)gamma;
			cmdData[11] = (byte)add;
			cmdData[12] = (byte)p_level;
			cmdData[13] = (byte)start;
			cmdData[14] = (byte)e_level;

            sendMiniCmdWithCheck(devIndex, subIndex, subPort, miniIndex, cmdData, 32);
		}

		// 设置 MCU 显示参数
		public void setMcuDispParameter(int[] paras)
        {
            
			// 解码器全局操作
			int subIndex = 0;
			int subPort = 0;
			int miniIndex = -1;

            setMCUConfigEnable(subIndex, subPort, miniIndex);
			

			int devIndex = getActiveDevIndex();

			if (devIndex < 0)
			{
				return;
			}

			byte[] cmdData = new byte[256];

			cmdData[2] = 0x65;
			cmdData[8] = (byte)(paras[0]);		// 整体亮度
			cmdData[9] = (byte)(paras[1]);		// 通道 R 亮度
			cmdData[10] = (byte)(paras[2]);		// 通道 G 亮度
			cmdData[11] = (byte)(paras[3]);		// 通道 B 亮度
			cmdData[12] = (byte)(paras[4]);		// 通道 W 亮度
			cmdData[13] = (byte)(paras[5]);		// Gamma 值

			sendMiniCmdWithCheck(devIndex, subIndex, subPort, miniIndex, cmdData, 32);
		}

        // 设置MCU配置使能
        public void setMCUConfigEnable(int subIndex, int subPort, int miniIndex)
        {
            int devIndex = getActiveDevIndex();

            if (devIndex < 0)
            {
                return;
            }

            byte[] cmdData = new byte[256];

            cmdData[2] = 0x71;
            cmdData[8] = 0xAA;
            

            sendMiniCmdWithCheck(devIndex, subIndex, subPort, miniIndex, cmdData, 32);

            Thread.Sleep(100);
        }

        public byte[] getMCUConfig(int subIndex, int subPortIndex, int miniIndex, int addr)
        {
            setMCUConfigEnable(subIndex, subPortIndex, miniIndex);

            int devIndex = getActiveDevIndex();

            if (devIndex < 0)
            {
                return new byte[64];
            }

            byte[] cmdData = new byte[256];

            cmdData[2] = 0x72;		// 命令

            cmdData[8] = (byte)(addr & 0xff);
            cmdData[9] = 0x37;
            cmdData[10] = 0x20;
            cmdData[11] = 0x00;

            int cmdCommNum = sendMiniCmdWithCheck(devIndex, subIndex, subPortIndex, miniIndex, cmdData, 32);

            byte[] ackData = new byte[256];
            bool success = getMiniAck(devIndex, subIndex, subIndex, ackData, cmdCommNum);

            byte[] reslut = new byte[32];
            if (success == true)
            {
                for (int i = 0; i < 32; i++)
                {
                    reslut[i] = ackData[i + 12];
                }
            }

            return reslut;
        }

		#endregion

		#region LedDebug
        // 获取Debug用的分控数量
        public int getSubCount()
        {
            int count = out_h % 8 == 0 ? out_h / 8 : out_h / 8 + 1;
            if(count < 0) count = 0;
            if(count > 255) count = 255;
            return count;
        }
		// 设置/取消设置有效的分控号、端口号
		public void ledDebugSetEffectSubAndPort(string statusStr)
		{
			try
			{
				// 取消分控、端口有效性设置
				if (statusStr.Trim().Equals("0:11111111"))
				{
					effectLedDebugSub = 0;
					effectLedDebugPort = 0;
				}
				// 生效分控、端口有效性设置
				else
				{
					string[] subStrArray = statusStr.Split(',');

					foreach (string subStr in subStrArray)
					{
						string[] s = subStr.Split(':');
						string subIndex = s[0];
						string portStatus = s[1];
						if (!portStatus.Trim('0').Equals(""))
						{
							int tmpEffectSubIndex = int.Parse(subIndex);
							int tmpEffectPortIndex = 0;
							if (portStatus.Equals("11111111"))
							{
								// 设置当前有效的分控、端口
								effectLedDebugSub = tmpEffectSubIndex;
								effectLedDebugPort = tmpEffectPortIndex;
								return;
							}
							else
							{
								for (int i = 0; i < portStatus.Length; i++)
								{
									if (portStatus[i] == '1')
									{
										tmpEffectPortIndex = i + 1;
										// 设置当前有效的分控、端口
										effectLedDebugSub = tmpEffectSubIndex;
										effectLedDebugPort = tmpEffectPortIndex;
										return;
									}
								}
							}
						}
					}
				}
			}
			catch (Exception e)
			{
				typeLog("[Error] " + e.Message + e.StackTrace);
				effectLedDebugSub = 0;
				effectLedDebugPort = 0;
			}
		}

		#endregion

		#region 硬件设置前后置处理

		// 硬件配置操作前处理
		public void configPreTreat()
		{
			// 备份当前状态
			backup_TopologyDetectEnable = topologyDetectEnable;
			backup_captureSendState = captureSendState;

			// 暂停在线检测
			setTopologyDetectEnable(false);

			// 暂停显示数据下发
			captureSendState = CaptureSendState.Stop;

			// 等待在线检测实际停止
			int waitLoops = 0;
			while (true)
			{
				if (topologyDetect_Processing)
				{
					Thread.Sleep(2);
					waitLoops++;
					if (waitLoops > 500)
					{
						typeLog("[Error] configPreTreat, waitTime > 1000", LogFile.Normal, LogTarget.Both);
						break;
					}
				}
				else
				{
					//typeLog("configPreTreat, TopoDetect stopped", LogFile.Normal, LogTarget.CmdOnly);
					break;
				}
			}
		}

		// 硬件配置操作后处理
		public void configPostTreat()
		{
			// 视情况恢复在线检测
			setTopologyDetectEnable(backup_TopologyDetectEnable);

			// 视情况恢复显示数据下发
			captureSendState = backup_captureSendState;
		}

		#endregion

        #region 用户通道模式设置

        public void setLowestGray(int gray)
        {
            this.lowestGray = gray;
            if (lowestGrayChangedHandle != null)
            {
                lowestGrayChangedHandle.Invoke();
            }
        }

        public int getLowestGray()
        {
            return this.lowestGray;
        }
        #endregion

        #region ArtNet

        //设置基准设备链信息
        public void SetBaseArtDevChain(ArtDeviceChain baseChain) {
            if (baseChain != null) {
                BaseArtDevDetailList = baseChain.Clone();
            }
        }

        //获取基准设备链信息
        public ArtDeviceChain GetBaseArtDevChain() {
            if (BaseArtDevDetailList == null) {
                return null;
            }
            return BaseArtDevDetailList.Clone();
        }

        //设置重新检测UID阈值与延时
        public void SetRequeryCondition(float threshold, int delaySec) {
            if (threshold <= 1 && threshold > 0 && delaySec >= 0) {
                ArtSubQueryThreshold = threshold;
                ArtSubQueryDelaySec = delaySec;
            }
        }

        //设置重新检测UID后等待时间
        public void SetRequeryWaitTime(int waittime) {
            if (waittime >= 0) {
                ArtRequeryWaitTime = waittime;
            }
        }

        //获取重检测状态 - 是否有检测信息
        public bool HasRequeryInfo() {
            return requeryOnce;
        }

        //设置检测信息提取状态
        public bool GetRequeryState() {
            return requeryInfoTold;
        }

        //设置检测信息提取状态
        public void SetRequeryState(bool state) {
            requeryInfoTold = state;
        }

        //强制重检测
        public void ForceRequery() {
            requeryTime = 0;
        }

        //获取Universe列表
        public List<int> GetUnvList() {
            lock (artDevListLock) {
                if (ArtDevList == null) {
                    return null;
                }
                var list = new List<int>();
                foreach (var net in ArtDevList.Keys) {
                    var infoList = ArtDevList[net];
                    foreach (var info in infoList) {
                        int unv = info.universe;

                        list.Add(unv);
                    }
                }
                //list.Sort();
                return list;
            }
        }

        //获取指定UID所在的Universe
        public int GetSpecificUnv(string uid) {
            var uidList = GetUIDList();
            if (uidList == null || uidList.Count == 0) {
                return -1;
            }
            var unv = -1;
            foreach (var item in uidList) {
                var list = item.Value;
                if (list.Contains(uid)) {
                    unv = item.Key;
                    break;
                }
            }
            return unv;
        }

        //获取UID列表-以Universe分类
        public Dictionary<int, List<string>> GetUIDList() {
            lock (artDevListLock) {
                if (ArtDevList == null) {
                    return null;
                }
                var dic = new Dictionary<int, List<string>>();
                foreach (var net in ArtDevList.Keys) {
                    var infoList = ArtDevList[net];
                    foreach (var info in infoList) {
                        var unv = info.universe;
                        if (info.UIDList != null) {
                            dic[unv] = info.UIDList.ToList();
                        } else {
                            dic[unv] = new List<string>();
                        }
                    }
                }

                return dic;
            }
        }

        //获取指定UID设备的详细信息
        public ArtDeviceInfo GetArtDevInfo(string uid) {
            if (ArtDevDetailList == null) {
                return null;
            }
            var dic = ArtDevDetailList.DetailList;
            if (dic == null || dic.Count == 0) {
                return null;
            }
            foreach (var portList in dic.Values) {
                foreach (var portInfo in portList) {
                    foreach (var devInfo in portInfo.DeviceList) {
                        if (devInfo != null) {
                            if (devInfo.UID == uid) {
                                return devInfo;
                            }
                        }
                    }
                }
            }
            return null;
        }

        //获取以分控和端口分类的UID信息 每个端口用Universe表示
        public Dictionary<int, Dictionary<int, List<string>>> GetArtSubUnvUIDList() { // 暂定
            lock (artDevListLock) {
                //var list = GetUnvList();
                //if (list == null || list.Count == 0) {
                //    return null;
                //}
                var uidList = GetUIDList();
                if (uidList == null || uidList.Count == 0) {
                    return null;
                }
                var dic = new Dictionary<int, Dictionary<int, List<string>>>();
                for (var i = 0; i < ArtDevList.Count; i++) {
                    var sub = i + 1;
                    if (!dic.ContainsKey(sub)) {
                        dic[sub] = new Dictionary<int, List<string>>();
                    }
                    foreach (var list in ArtDevList.Values) {
                        if (list == null || list.Count == 0) {
                            continue;
                        }
                        for (var j = 0; j < list.Count; j++) {
                            var unv = list[j].universe;
                            if (uidList.ContainsKey(unv)) {
                                dic[sub][unv] = uidList[unv];
                            }
                        }
                    }
                }
                return dic;
            }
        }

        //获取与指定基准的Art设备链比较的Art设备错误统计
        public ArtFaultTotal CompareWithBaseArtDevChain(ArtDeviceChain baseChain) {
            var faultTotal = new ArtFaultTotal();
            if (baseChain == null) {
                return null;
            }
            var baseDetail = baseChain.DetailList;
            if (baseDetail == null || baseDetail.Count == 0) {
                return null;
            }
            var currDic = GetArtSubUnvUIDList();
#if false // test
            currDic = new Dictionary<int, Dictionary<int, List<string>>>();
            currDic[1] = new Dictionary<int, List<string>>();
            for (var i = 0; i < 8;i++ ) {
                currDic[1][i] = new List<string>();
            }
            currDic[1][2] = new List<string>() { "02F0189D0085", "02F0189D0083", "02F0189D0081", "02F0189D007F", "02F0189D007D", "02F0189D007B",};
#endif
            var faultList = new List<ArtDeviceFault>();
            foreach (var item in baseDetail) {
                var sub = item.Key;
                var portDetails = item.Value;
                if (currDic == null || (currDic != null && !currDic.ContainsKey(sub))) {
                    foreach (var detail in portDetails) {
                        var port = detail.PortID;
                        var unv = detail.Universe;
                        var devList = detail.DeviceList;
                        for (var i = 0; i < devList.Count; i++) {
                            var fault = new ArtDeviceFault();
                            fault.SubID = sub;
                            fault.PortID = port;
                            fault.FaultType = ArtFaultType.Undetect;
                            fault.FaultTime = DateTime.Now;
                            fault.UID = devList[i].UID;
                            faultList.Add(fault);
                        }
                    }
                    continue;
                }
                var currSub = currDic[sub];
                foreach (var detail in portDetails) {
                    var port = detail.PortID;
                    var unv = detail.Universe;
                    if (!currSub.ContainsKey(unv)) { 
                        // 理论上不会出现分控universe变更
                        continue;
                    }
                    var devList = detail.DeviceList;
                    var uidList = currSub[unv];
                    var baseCnt = devList.Count;
                    var currCnt = uidList.Count;
                    var minCnt = Math.Min(baseCnt, currCnt);
                    for (var i = 0; i < minCnt; i++) {
                        if (devList[i].UID != uidList[i]) {
                            var fault = new ArtDeviceFault();
                            fault.SubID = sub;
                            fault.PortID = port;
                            fault.FaultType = ArtFaultType.Undetect;
                            fault.FaultTime = DateTime.Now;
                            fault.UID = devList[i].UID;
                            faultList.Add(fault);
                        }
                    }
                    if (baseCnt > currCnt) {
                        for (var j = minCnt; j < baseCnt; j++) {
                            var fault = new ArtDeviceFault();
                            fault.SubID = sub;
                            fault.PortID = port;
                            fault.FaultType = ArtFaultType.Undetect;
                            fault.FaultTime = DateTime.Now;
                            fault.UID = devList[j].UID;
                            faultList.Add(fault);
                        }
                    } else if (baseCnt < currCnt) {
                        for (var k = minCnt; k < currCnt; k++) {
                            var fault = new ArtDeviceFault();
                            fault.SubID = sub;
                            fault.PortID = port;
                            fault.FaultType = ArtFaultType.Undetect;
                            fault.FaultTime = DateTime.Now;
                            fault.UID = uidList[k];
                            faultList.Add(fault);
                        }
                    }
                }
            }
            faultTotal.Details = faultList;
            return faultTotal;
        }

        //查找art分控 - ArtPoll
        public void QueryArtSubControl() {
            ArtDevList = new Dictionary<byte, List<ArtPortInfo>>();
            for (var i = 0; i < netDeviceList.Count; i++) {
                var dev = netDeviceList[i];
                if (!dev.activeStatus) {
                    continue;
                }
                var pending = true;
                sendArtPollPacket(dev, dev.BroadcastIP, (ret, obj) => {
                    pending = false;
                });
                while (pending) {
                    Thread.Sleep(1);
                }
                break;
            }
        }

        //查找uid - ArtToDRequest
        public void QueryUIDList(int unv) {
            for (var i = 0; i < netDeviceList.Count; i++) {
                var dev = netDeviceList[i];
                if (!dev.activeStatus) {
                    continue;
                }
                var pending = true;
                sendArtToDRequestPacket(dev, unv, (ret, obj) => {
                    pending = false;
                });
                while (pending) {
                    Thread.Sleep(1);
                }
                break;
            }
        }

        //重新查找所有uid - ArtToDControl
        public void RequeryUIDList(int unv) {
            for (var i = 0; i < netDeviceList.Count; i++) {
                var dev = netDeviceList[i];
                if (!dev.activeStatus) {
                    continue;
                }
                var pending = true;
                sendArtToDControlPacket(dev, unv, (ret, obj) => {
                    pending = false;
                });
                while (pending) {
                    Thread.Sleep(1);
                }
                break;
            }
        }

        //标记设备 - IDENTIFY_DEVICE
        public void IdentifyArtDevice(string uid, int unv, bool enable) {
            var para = new RDMParaIndetify(uid, (byte)(enable ? 1 : 0));
            var rdmPacket = buildRDMPacket(Catergory.IDENTIFY_DEVICE, CommandType.SET_COMMAND, uid, para);

            for (var i = 0; i < netDeviceList.Count; i++) {
                var dev = netDeviceList[i];
                if (!dev.activeStatus) {
                    continue;
                }
                var pending = true;
                sendArtRDMPacket(dev, rdmPacket, unv, (ret, obj) => {
                    pending = false;
                });
                while (pending) {
                    Thread.Sleep(1);
                }
                break;
            }
        }

        //查设备标签 - DEVICE_LABEL
        public string ReadDeviceLabel(string uid, int unv) {
            var rdmPacket = buildRDMPacket(Catergory.DEVICE_LABEL, CommandType.GET_COMMAND, uid);
            for (var i = 0; i < netDeviceList.Count; i++) {
                var dev = netDeviceList[i];
                if (!dev.activeStatus) {
                    continue;
                }
                var pending = true;
                sendArtRDMPacket(dev, rdmPacket, unv, (ret, obj) => {
                    pending = false;
                });
                while (pending) {
                    Thread.Sleep(1);
                }
                break;
            }
            if (readArtPacket is ArtRDM) {
                var rdmRec = (ArtRDM)readArtPacket;
                var obj = getArtRDMReply(rdmRec);
                if (obj is string) {
                    return (string)obj;
                } 
            }
            return "";
        }

        //查找软件版本 - SOFTWARE_VERSION_LABEL
        public string ReadSoftwareVersionLabel(string uid, int unv) {
            var rdmPacket = buildRDMPacket(Catergory.SOFTWARE_VERSION_LABEL, CommandType.GET_COMMAND, uid);
            for (var i = 0; i < netDeviceList.Count; i++) {
                var dev = netDeviceList[i];
                if (!dev.activeStatus) {
                    continue;
                }
                var pending = true;
                sendArtRDMPacket(dev, rdmPacket, unv, (ret, obj) => {
                    pending = false;
                });
                while (pending) {
                    Thread.Sleep(1);
                }
                break;
            }
            if (readArtPacket is ArtRDM) {
                var rdmRec = (ArtRDM)readArtPacket;
                var obj = getArtRDMReply(rdmRec);
                if (obj is string) {
                    return (string)obj;
                }
            }
            return "";
        }

        //查设备信息 - DEVICE_INFO
        public ArtDeviceInfo ReadDeviceInfo(string uid, int unv) {
            var rdmPacket = buildRDMPacket(Catergory.DEVICE_INFO, CommandType.GET_COMMAND, uid);
            for (var i = 0; i < netDeviceList.Count; i++) {
                var dev = netDeviceList[i];
                if (!dev.activeStatus) {
                    continue;
                }
                var pending = true;
                sendArtRDMPacket(dev, rdmPacket, unv, (ret, obj) => {
                    pending = false;
                });
                while (pending) {
                    Thread.Sleep(1);
                }
                break;
            }
            if (readArtPacket is ArtRDM) {
                var rdmRec = (ArtRDM)readArtPacket;
                var obj = getArtRDMReply(rdmRec);
                if (obj is ArtDeviceInfo) {
                    var res = obj as ArtDeviceInfo;
                    return res;
                }
            }
            return null;
        }

        //查厂家信息 - MANUFACTURER_LABEL
        public string ReadManufacturerLabel(string uid, int unv) {
            var rdmPacket = buildRDMPacket(Catergory.MANUFACTURER_LABEL, CommandType.GET_COMMAND, uid);
            for (var i = 0; i < netDeviceList.Count; i++) {
                var dev = netDeviceList[i];
                if (!dev.activeStatus) {
                    continue;
                }
                var pending = true;
                sendArtRDMPacket(dev, rdmPacket, unv, (ret, obj) => {
                    pending = false;
                });
                while (pending) {
                    Thread.Sleep(1);
                }
                break;
            }
            if (readArtPacket is ArtRDM) {
                var rdmRec = (ArtRDM)readArtPacket;
                var obj = getArtRDMReply(rdmRec);
                if (obj is string) {
                    return (string)obj;
                }
            }
            return "";
        }

        //传感器类型 - SENSOR_DEFINITION
        public ArtSensorDef ReadSensorDefinition(string uid, int unv, int id) {
            var para = new RDMParaSensor(uid, (byte)id);
            var rdmPacket = buildRDMPacket(Catergory.SENSOR_DEFINITION, CommandType.GET_COMMAND, uid, para);
            for (var i = 0; i < netDeviceList.Count; i++) {
                var dev = netDeviceList[i];
                if (!dev.activeStatus) {
                    continue;
                }
                var pending = true;
                sendArtRDMPacket(dev, rdmPacket, unv, (ret, obj) => {
                    pending = false;
                });
                while (pending) {
                    Thread.Sleep(1);
                }
                break;
            }
            if (readArtPacket is ArtRDM) {
                var rdmRec = (ArtRDM)readArtPacket;
                var obj = getArtRDMReply(rdmRec);
                if (obj is ArtSensorDef) {
                    var item = (ArtSensorDef)obj;
                    item.ID = id;
                    return item;
                }
            }
            return null;
        }

        //传感器值 - SENSOR_VALUE
        public ArtSensorValue ReadSensorValue(string uid, int unv, int id) {
            var para = new RDMParaSensor(uid, (byte)id);
            var rdmPacket = buildRDMPacket(Catergory.SENSOR_VALUE, CommandType.GET_COMMAND, uid, para);
            for (var i = 0; i < netDeviceList.Count; i++) {
                var dev = netDeviceList[i];
                if (!dev.activeStatus) {
                    continue;
                }
                var pending = true;
                sendArtRDMPacket(dev, rdmPacket, unv, (ret, obj) => {
                    pending = false;
                });
                while (pending) {
                    Thread.Sleep(1);
                }
                break;
            }
            if (readArtPacket is ArtRDM) {
                var rdmRec = (ArtRDM)readArtPacket;
                var obj = getArtRDMReply(rdmRec);
                if (obj is ArtSensorValue) {
                    var item = (ArtSensorValue)obj;
                    item.ID = id;
                    return item;
                }
            }
            return null;
        }

        //读DMX地址 - DMX_START_ADDRESS
        public int ReadArtDmxAddr(string uid, int unv) {
            var rdmPacket = buildRDMPacket(Catergory.DMX_START_ADDRESS, CommandType.GET_COMMAND, uid);
            for (var i = 0; i < netDeviceList.Count; i++) {
                var dev = netDeviceList[i];
                if (!dev.activeStatus) {
                    continue;
                }
                var pending = true;
                sendArtRDMPacket(dev, rdmPacket, unv, (ret, obj) => {
                    pending = false;
                });
                while (pending) {
                    Thread.Sleep(1);
                }
                break;
            }
            if (readArtPacket is ArtRDM) {
                var rdmRec = (ArtRDM)readArtPacket;
                var obj = getArtRDMReply(rdmRec);
                if (obj is int) {
                    return (int)obj;
                }
            }
            return 0;
        }

        //写DMX地址 - DMX_START_ADDRESS
        public void SetArtDmxAddr(string uid, int unv, int addr) {
            var para = new RDMParaDMXAddr(uid, (ushort)addr);
            var rdmPacket = buildRDMPacket(Catergory.DMX_START_ADDRESS, CommandType.SET_COMMAND, uid, para);

            for (var i = 0; i < netDeviceList.Count; i++) {
                var dev = netDeviceList[i];
                if (!dev.activeStatus) {
                    continue;
                }
                var pending = true;
                sendArtRDMPacket(dev, rdmPacket, unv, (ret, obj) => {
                    pending = false;
                });
                while (pending) {
                    Thread.Sleep(1);
                }
                break;
            }
        }

        //查找所有uid
        public void QueryUIDListTotal() {
            var list = GetUnvList();
            if (list != null && list.Count > 0) {
                for (var i = 0; i < list.Count; i++) {
                    var tmpUnv = list[i]; 
                    //if (tmpUnv % 8 != 0) {
                    //    continue;
                    //}
                    QueryUIDList(tmpUnv);
                }
                RestoreArtDeviceChainDetail();
            }
            printArtSubUnvUIDList();
        }

        //查找所有uid
        public void QueryUIDListTotal_Sub()
        {
            for (var i = 0; i < netDeviceList.Count; i++)
            {
                var dev = netDeviceList[i];
                if (dev.activeStatus == false)
                {
                    continue;
                }
                for (int j = 0; j < dev.subCtrlList_0.Count(); j++)
                {
                    int sub_no = dev.subCtrlList_0[j].mySubCtrlDepth;// -1;
                    int universe_start = 8 * sub_no;
                    for (int k = 0; k < 8; k++)
                    {
                        int universe_now = universe_start + k;
                        ArtDevList_uu[universe_now].UIDList = null;
                        var pending = true;
                        sendArtToDRequestPacket(dev, universe_now, (ret, obj) => { pending = false; });
                        while (pending)
                        {
                            Thread.Sleep(1);
                        }
                    }
                }
            }
        }

        //使用RDM模式查询单个灯具
        public LampStatePara checkOneLamp_Rdm(int universe, string UID)
        {
            LampStatePara lampStatePara = null;
            ArtDeviceInfo devInfo = null;
            int rrr = 0;

            //出错重检3次
            for (rrr = 0; rrr < 3; rrr++)
            {
                //ArtDeviceInfo devInfo = ReadArtDevDetail(UID, universe_now, true);
                devInfo = ReadArtDevDetail_iColor(UID, universe);
                if (devInfo != null)
                    break;
            }

            lampStatePara = new LampStatePara();
            lampStatePara.SubIndex = universe >> 3;
            lampStatePara.PortIndex = universe & 0x07;
            lampStatePara.RdmSeg = UID;

            if (rrr >= 3)
            {
                lampStatePara.detect_ok = false;

                return lampStatePara;
            }

            lampStatePara.detect_ok = true;
            lampStatePara.LampChannel = devInfo.LampChannel;
            lampStatePara.LampIndex = devInfo.ChipID;
            lampStatePara.Version = devInfo.SoftwareVersionID;
            lampStatePara.DmxAddr = devInfo.DMXAddr;

            var sensorList = devInfo.SensorList;
            if (sensorList != null && sensorList.Count != 0)
            {
                for (var ss = 0; ss < sensorList.Count; ss++)
                {
                    var sensor = sensorList[ss];
                    var def = sensor.Definition;
                    var val = sensor.Value;
                    if (def != null && val != null)
                    {
                        var type = def.Type;
                        var value = val.CurrVal;
                        switch (type)
                        {
                            case SensorType.Current:
                                lampStatePara.Current = value;
                                break;
                            case SensorType.Voltage:
                                lampStatePara.Voltage = value;
                                break;
                            case SensorType.Temperature:
                                lampStatePara.Temperature = value;
                                break;
                            case SensorType.OutError:
                                lampStatePara.ErrorInfo = value;
                                break;
                        }
                    }
                }
            }
            return lampStatePara;

        }

        //重新查找所有uid
        public void RequeryUIDListTotal() {
            var list = GetUnvList();
            if (list != null && list.Count > 0) {
                for (var i = 0; i < list.Count; i++) {
                    var tmpUnv = list[i];
                    //if (tmpUnv % 8 != 0) {
                    //    continue;
                    //}
                    RequeryUIDList(tmpUnv); 
                }
            }
        }

        public void RequeryUIDListTotal_Sub()
        {
            for (var i = 0; i < netDeviceList.Count; i++)
            {
                var dev = netDeviceList[i];
                if (dev.activeStatus == false)
                {
                    continue;
                }
                for (int j = 0; j < dev.subCtrlList_0.Count(); j++)
                {
                    int sub_no = dev.subCtrlList_0[j].mySubCtrlDepth;// -1;
                    int universe_start = 8 * sub_no;

                    var pending = true;
                    sendArtToDControlPacket(dev, universe_start, (ret, obj) => {pending = false;});
                    while (pending)
                    {
                        Thread.Sleep(1);
                    }
                }
            }
        }

        //读指定Art设备详细信息
        public ArtDeviceInfo ReadArtDevDetail(string uid, int unv, bool readSensorVal = false) {
            var info = ReadDeviceInfo(uid, unv);
            if (info == null) {
                return null;
            }
            info.UID = uid;
            //info.DeviceLabel = ReadDeviceLabel(uid, unv);
            //info.ManufactorLabel = ReadManufacturerLabel(uid, unv);
            info.SoftwareVersionLabel = ReadSoftwareVersionLabel(uid, unv);
            if (info.SensorCount != 0) {
                info.SensorList = new List<ArtSensorInfo>();
                for (var i = 0; i < info.SensorCount; i++) {
                    var sensorInfo = new ArtSensorInfo();
                    var def = ReadSensorDefinition(uid, unv, i);
                    if (def == null) {
                        break;
                    }
                    sensorInfo.Definition = def;
                    if (readSensorVal) {
                        var val = ReadSensorValue(uid, unv, i);
                        if (val == null) {
                            break;
                        }
                        val.Prefix = def.Prefix;
                        sensorInfo.Value = val; 
                    }
                    info.SensorList.Add(sensorInfo);
                }
            }
            return info;
        }

        //读指定Art设备详细信息_iColor扩展协议
        public ArtDeviceInfo ReadArtDevDetail_iColor(string uid, int unv)
        {
            var rdmPacket = buildRDMPacket(Catergory.ICOLOR_INFO, CommandType.GET_COMMAND, uid);

            for (var i = 0; i < netDeviceList.Count; i++)
            {
                var dev = netDeviceList[i];
                if (!dev.activeStatus)
                {
                    continue;
                }
                readArtPacket = null;
                var pending = true;
                sendArtRDMPacket(dev, rdmPacket, unv, (ret, obj) =>
                {
                    pending = false;
                });
                while (pending)
                {
                    Thread.Sleep(1);
                }
                break;
            }
            if (readArtPacket is ArtRDM)
            {
                var rdmRec = (ArtRDM)readArtPacket;
                var obj = getArtRDMReply(rdmRec);
                if (obj is ArtDeviceInfo)
                {
                    var res = obj as ArtDeviceInfo;
                    return res;
                }
            }
            return null;
        }

        //读Art设备链详细信息
        public void ReadArtDeviceChainDetail() { //基于已有的uid列表获取灯具信息
            //RestartQueryUnvUidList();
            var devChain = new ArtDeviceChain();
            var dic = devChain.DetailList;
            var subList = GetArtSubUnvUIDList();
            if (subList == null) {
                return;
            }
            foreach (var item in subList) {
                var sub = item.Key;
                var portList = item.Value;
                if (!dic.ContainsKey(sub)) {
                    dic[sub] = new List<ArtPortDetail>();
                }
                var portCnt = 0;
                foreach(var pair in portList){
                    portCnt++;
                    var port = portCnt;
                    var unv = pair.Key;
                    var uidList = pair.Value;
                    var detail = new ArtPortDetail();
                    detail.SubID = sub;
                    detail.PortID = port;
                    detail.Universe = unv;
                    detail.DeviceList = new List<ArtDeviceInfo>();
                    foreach (var uid in uidList) {
                        var info = ReadArtDevDetail(uid, unv);
                        if (info != null) {
                            detail.DeviceList.Add(info);
                        }
                    }
                    dic[sub].Add(detail);
                }
            }
            devChain.DetailList = dic;

            ArtDevDetailList = devChain.Clone();
        }

        //发起重新查询Art设备Universe和UID列表
        public void RestartQueryUnvUidList() {
            QueryArtSubControl();
            RequeryUIDListTotal();
            Thread.Sleep(ArtRequeryWaitTime * 1000);
            QueryUIDListTotal();
        }

        //重置当前设备链详情，仅包含灯具UID信息
        public void RestoreArtDeviceChainDetail() {
            var subList = GetArtSubUnvUIDList();
            if (subList == null) {
                return;
            }
            ArtDevDetailList = new ArtDeviceChain();
            var dic = ArtDevDetailList.DetailList;
            foreach (var item in subList) {
                var sub = item.Key;
                var portList = item.Value;
                if (!dic.ContainsKey(sub)) {
                    dic[sub] = new List<ArtPortDetail>();
                }
                var portCnt = 0;
                foreach (var pair in portList) {
                    portCnt++;
                    var port = portCnt;
                    var unv = pair.Key;
                    var uidList = pair.Value;
                    var detail = new ArtPortDetail();
                    detail.SubID = sub;
                    detail.PortID = port;
                    detail.Universe = unv;
                    detail.DeviceList = new List<ArtDeviceInfo>();
                    foreach (var uid in uidList) {
                        var info = new ArtDeviceInfo();
                        info.UID = uid;
                        detail.DeviceList.Add(info);
                    }
                    dic[sub].Add(detail);
                }
            }
            ArtDevDetailList.DetailList = dic;
        }

        //获取当前设备链信息
        public ArtDeviceChain GetCurrArtDeviceDetailList() {
            if (ArtDevDetailList == null) {
                return null;
            }
            return ArtDevDetailList.Clone();
        }
        #endregion

        #endregion

        /****************************************************************************************************/
	}

	#region 内部使用的类和枚举

	// 在线检测模式
	public enum DetectMode
	{
		AllDetect = 0,		// 全检测 - 检测所有级联设备
		ActiveDetect = 1	// 活跃检测 - 检测1分控+1附属设备, 用于确定是否有活跃网口
	}

	// 截屏发送状态
	public enum CaptureSendState
	{
		Run = 0,			// 运行
		Stop = 1,			// 停止
		Pause = 2			// 暂停
	}

	// 异形映射（布线文件）数据源类型
	public enum MapSrcType
	{
		NoMap = 0,
		UseBin = 1,
		UseJson = 2
	}

	// 异形映射模式
	public enum MapMode
	{
        Baned = 0,				// 禁用模式
		FourPure = 1,			// 四色纯模式
		FourIncrease = 2,		// 四色增强模式
		EachPoint = 3,			// 逐点模式, 由 Json 数据指定
		CustomByPort = 4,		// 自定义模式, 由 lwf 文件中的端口映射指定
		CustomByPara = 5,		// 自定义模式, 由软件内部变量指定（分控设置-灯具类型）
		CustomByChannel = 6		// 自定义模式, 由 lwf 文件中的通道映射指定
	}

	// 颜色模式
	public class ColorMode
	{
		public int addrMapType = 0;
		public string colorString = "";
		public int[] colorArray = null;
	}

	// 批量读取Flash - 事件参数
	public class ReadEventArgs : EventArgs
	{
		public int processValue = 0;
	}

	// 批量写入Flash - 事件参数
	public class WriteEventArgs : EventArgs
	{
		public int processValue = 0;
		public string operation = "";
		public string errorMsg = "";

		public int current = 0;
		public int total = 0;

		public WriteEventArgs()
		{
			processValue = 0;
		}

		public WriteEventArgs(int processValue, string operation)
		{
			this.processValue = processValue;
			this.operation = operation;
		}

		public WriteEventArgs(int processValue, string operation, string errorMsg)
		{
			this.processValue = processValue;
			this.operation = operation;
			this.errorMsg = errorMsg;
		}
	}

	// id-mac 对
	public class IdMacPair
	{
		public int startId = -1;

		public int endId = -1;

		public string mac = "";
	}

	// mac 映射表
	public class MacTable
	{
		public int id;

		public string macIndex;

		public int offset;
	}

	// 声明的主控信息
	public class DMainControl
	{
		public int declareSubNum = -1;		// 声明的分控数

		public List<DSubControl> declareSubControlList = null;

		public DMainControl()
		{
			declareSubControlList = new List<DSubControl>();
		}

		public DMainControl Clone()
		{
			DMainControl cloneMainControl = new DMainControl();
			cloneMainControl.declareSubNum = declareSubNum;
			cloneMainControl.declareSubControlList = new List<DSubControl>();
			for (int i = 0; i < declareSubControlList.Count; i++)
			{
				cloneMainControl.declareSubControlList.Add(declareSubControlList[i].Clone());
			}

			return cloneMainControl;
		}
	}

	// 声明的分控信息
	public class DSubControl
	{
		public int mainControlIndex = -1;	// 所属的主控索引

		public int subControlIndex = -1;	// 分控索引

		public int declarePortNum = -1;		// 声明的分控端口数

		public int declareLampNum = -1;		// 声明的总灯具数

		public DSubControl Clone()
		{
			DSubControl cloneSubControl = new DSubControl();
			cloneSubControl.mainControlIndex = mainControlIndex;
			cloneSubControl.subControlIndex = subControlIndex;
			cloneSubControl.declarePortNum = declarePortNum;
			cloneSubControl.declareLampNum = declareLampNum;
			return cloneSubControl;
		}
	}

	// 所选的分控信息
	public class SelectedSubInfo
	{
		public bool overall = false;

		public int startIndex = -1;

		public int endIndex = -1;

		public List<int> sourceSubIndexList = new List<int>();
		public List<int> targetSubIndexList = new List<int>();

		public bool withPreTreat = false;

		public bool useAddrMapOffsetTable = false;

		public List<AddrMapOffset> addrMapOffsetList = null;

		public bool enableManualOffset = false;

		public int manualOffset = -1;
	}

	// 布线配置表
	[JsonObject(MemberSerialization.OptIn)]
	public class AddrMapOffset
	{
		[JsonProperty]
		public int startIndex = -1;

		[JsonProperty]
		public int endIndex = -1;

		[JsonProperty]
		public int offsetX = 0;

		[JsonProperty]
		public int offsetY = 0;
	}

	// 下载布线时, 数据预处理的参考信息
	public class StartCut
	{
		public int startX;

		public int startY;

		public int cutX;

		public int cutY;
	}

	// Tick 二元组
	public class TickPair
	{
		public long tick1;

		public long tick2;

		public TickPair(long value1, long value2)
		{
			tick1 = value1;
			tick2 = value2;
		}
	}

	// 网卡信息
	public class NetworkInterfaceInfo
	{
		public bool isWireless = false;

		public string ipAddress = "null";

		public string name = "";

        public string mask = "";
	}

	// Socket发包模式
	public enum SocketSendMode
	{
		Sync = 0,	// 同步
		Asyn = 1	// 异步
	}

	internal class IntPair
	{
		public int val1 = 0;

		public int val2 = 0;

		public IntPair(int v1, int v2)
		{
			val1 = v1;
			val2 = v2;
		}
	}

    // 网络速度配置
    public enum NetSpeed
    {
        GigaByte = 1,	// 千兆
        HundredMegaByte = 2	// 百兆
    }

    // 在线检测方式
    public enum DetectType {
        Normal,
        ArtNet,
    }

	#endregion
}
